雙節(jié)旅游人如山,不如家中代碼閑。 學(xué)以致用加班少,王者榮耀家中玩。
小編日常工作中使用的是 Vue ,對(duì)于 React 只是做過(guò)簡(jiǎn)單的了解,并沒(méi)有做過(guò)深入學(xué)習(xí)。趁著這個(gè)雙節(jié)假期,小編決定好好學(xué)一學(xué) React ,今天這篇文章就是小編在學(xué)習(xí) React 之后,將 React 與 Vue 的用法做的一個(gè)對(duì)比,通過(guò)這個(gè)對(duì)比,方便使用 Vue 的小伙伴可以快速將 Vue 中的寫(xiě)法轉(zhuǎn)換為 React 的寫(xiě)法。
插槽,在React
中沒(méi)找到??
在使用Vue
的時(shí)候,插槽是一個(gè)特別常用的功能,通過(guò)定義插槽,可以在調(diào)用組件的時(shí)候?qū)⑼獠康膬?nèi)容傳入到組件內(nèi)部,顯示到指定的位置。在Vue
中,插槽分為默認(rèn)插槽,具名插槽和作用域插槽。其實(shí)不僅僅Vue
,在 React 中其實(shí)也有類(lèi)似插槽的功能,只是名字不叫做插槽,下面我將通過(guò)舉例來(lái)說(shuō)明。
默認(rèn)插槽
現(xiàn)在項(xiàng)目需要開(kāi)發(fā)一個(gè)卡片組件,如下圖所示,卡片可以指定標(biāo)題,然后卡片內(nèi)容可以用戶(hù)自定義,這時(shí)候?qū)τ诳ㄆ瑑?nèi)容來(lái)說(shuō),就可以使用插槽來(lái)實(shí)現(xiàn),下面我們就分別使用Vue
和React
來(lái)實(shí)現(xiàn)這個(gè)功能
Vue
實(shí)現(xiàn)
- 首先實(shí)現(xiàn)一個(gè)
card
組件,如下代碼所示
<div class="card">
<div class="card__title">
<span>{{ title }}</span>
</div>
<div class="card__body">
</div>
</div>
export default {
props: {
title: {
type: String,
default: ''
}
}
}
可以看到上面我們使用了,這個(gè)就是組件的默認(rèn)插槽,在使用組件的時(shí)候,傳入的內(nèi)容將會(huì)被放到
所在位置
- 在外部使用定義的
card
組件
<div>
<div>我將被放在card組件的默認(rèn)插槽里面</div>
</div>
import MyCard from '../components/card'
export default {
components: {
MyCard
}
}
如上代碼,就可以使用組件的默認(rèn)插槽將外部的內(nèi)容應(yīng)用到組件里面指定的位置了。
React
實(shí)現(xiàn)
雖然在React
里面沒(méi)有插槽的概念,但是React
里面也可以通過(guò)props.children
拿到組件標(biāo)簽內(nèi)部的子元素的,就像上面代碼`標(biāo)簽內(nèi)的子元素,通過(guò)這個(gè)我們也可以實(shí)現(xiàn)類(lèi)似
Vue`默認(rèn)插槽的功能,一起看看代碼。
- 使用
React
定義Card
組件
import React from 'react'
export interface CardProps {
title: string,
children: React.ReactNode
}
export default function(props: CardProps) {
return (
<div className="card">
<div className="card__title">
<span>{props.title}</span>
</div>
<div className="card__body">
{/**每個(gè)組件都可以獲取到 props.children。它包含組件的開(kāi)始標(biāo)簽和結(jié)束標(biāo)簽之間的內(nèi)容 */}
{props.children}
</div>
</div>
);
}
import React from 'react'
import Card from './components/Card'
export default function () {
return (
<div>
<div>我將被放在card組件的body區(qū)域內(nèi)容</div>
</div>
);
}
-
- 在外部使用
Card
組件
- 在外部使用
具名插槽
繼續(xù)以上面的Card
組件為例,假如我們現(xiàn)在需求發(fā)生了變化,組件的title
也可以使用插槽,這時(shí)候?qū)τ?code>Vue就可以使用具名插槽了,而React
也是有辦法實(shí)現(xiàn)的哦。
Vue實(shí)現(xiàn)
Vue
的具名插槽主要解決的是一個(gè)組件需要多個(gè)插槽的場(chǎng)景,其實(shí)現(xiàn)是為`添加
name`屬性來(lái)實(shí)現(xiàn)了。
- 我們就上面的需求對(duì)
card
組件進(jìn)行修改
<div class="card">
<div class="card__title">
<span v-if="title">{{ title }}</span>
</div>
<div class="card__body">
</div>
</div>
export default {
props: {
title: {
type: String,
default: ''
}
}
}
card
組件修改完之后,我們?cè)偃フ{(diào)整一下使用card
組件的地方
<div>
<span>這里是標(biāo)題</span>
<div>我將被放在card組件的默認(rèn)插槽里面</div>
</div>
import MyCard from '../components/card'
export default {
components: {
MyCard
}
}
React實(shí)現(xiàn)
React
連插槽都沒(méi)有, 更別提具名插槽了,但是沒(méi)有不代表不能模擬出來(lái)。對(duì)于React
的props
,我們不僅僅可以傳入普通的屬性,還可以傳入一個(gè)函數(shù),這時(shí)候我們就可以在傳入的這個(gè)函數(shù)里面返回JSX
,從而就實(shí)現(xiàn)了具名插槽的功能。
- 對(duì)原有的
Card
組件進(jìn)行修改
import React from 'react'
export interface CardProps {
title?: string,
// 加入了一個(gè)renderTitle屬性,屬性類(lèi)型是Function
renderTitle?: Function,
children: React.ReactNode
}
export default function(props: CardProps) {
const {title, renderTitle} = props
// 如果指定了renderTtile,則使用renderTitle,否則使用默認(rèn)的title
let titleEl = renderTitle ? renderTitle() : <span>{title}</span>
return (
<div className="card">
<div className="card__title">{titleEl}</div>
<div className="card__body">
{/**每個(gè)組件都可以獲取到 props.children。它包含組件的開(kāi)始標(biāo)簽和結(jié)束標(biāo)簽之間的內(nèi)容 */}
{props.children}
</div>
</div>
);
}
- 這時(shí)候就可以在外部自定義
title
了
import React from 'react'
import Card from './components/Card'
export default function () {
return (
<div>
{
return <span>我是自定義的標(biāo)題</span>
}
}>
<div>我將被放在card組件的body區(qū)域內(nèi)容</div>
</div>
);
}
作用域插槽
有時(shí)讓插槽內(nèi)容能夠訪(fǎng)問(wèn)子組件中才有的數(shù)據(jù)是很有用的,這個(gè)就是Vue
提供作用域插槽的原因。我們繼續(xù)使用上面的Card
組件為例,現(xiàn)在我基于上面的卡片組件開(kāi)發(fā)了一個(gè)人員信息卡片組件,用戶(hù)直接使用人員信息卡片組件就可以將人員信息顯示到界面中,但是在某些業(yè)務(wù)模塊需要自定義人員信息顯示方式,這時(shí)候我們就需要使用到作用域插槽了。
Vue
實(shí)現(xiàn)
- 實(shí)現(xiàn)用戶(hù)信息卡片組件,里面使用了作用域插槽
<div class="content">
<span>姓名: {{ userInfo.name }}</span>
<span>性別: {{ userInfo.sex }}</span>
<span>年齡: {{ userInfo.age }}</span>
</div>
import CustomCard from '../card'
export default {
components: {
CustomCard
},
data() {
return {
userInfo: {
name: '張三',
sex: '男',
age: 25
}
}
}
}
- 在外部使用人員信息組件
<div>
<div class="custom-user">
<ul>
<li>姓名: {{ userInfo.name }}</li>
<li>年齡: {{ userInfo.age }}</li>
</ul>
</div>
</div>
import UserCard from '../components/user-card'
export default {
components: {
UserCard
}
}
React
實(shí)現(xiàn)
在具名插槽那一小節(jié)我們通過(guò)給組件傳入了一個(gè)函數(shù),然后在函數(shù)中返回JSX
的方式來(lái)模擬了具名插槽,那么對(duì)于作用域插槽,我們依然可以使用函數(shù)的這種方式,而作用域插槽傳遞的參數(shù)我們可以使用給函數(shù)傳參的方式來(lái)替代
- 實(shí)現(xiàn)人員信息卡片組件
import React, { useState } from 'react'
import Card from './Card'
interface UserCardProps {
renderUserInfo?: Function
}
export interface UserInfo {
name: string;
age: number;
sex: string;
}
export default function(props: UserCardProps) {
const [userInfo] = useState({
name: "張三",
age: 25,
sex: "男",
});
const content = props.renderUserInfo ? (
props.renderUserInfo(userInfo)
) : (
<div>
<span>姓名: {userInfo.name}</span>
<span>年齡: {userInfo.age}</span>
<span>性別: {userInfo.sex}</span>
</div>
);
return
{content}
}
- 在外部使用人員信息卡片組件
import React from 'react'
import UserCard, { UserInfo } from "./components/UserCard";
export default function () {
return (
<div>
{
return (
<ul>
<li>姓名: {userInfo.name}</li>
</ul>
);
}}
>
</div>
);
}
Context
, React
中的provide/inject
通常我們?cè)陧?xiàng)目開(kāi)發(fā)中,對(duì)于多組件之間的狀態(tài)管理,在Vue
中會(huì)使用到Vuex
,在React
中會(huì)使用到redux
或者Mobx
,但對(duì)于小項(xiàng)目來(lái)說(shuō),使用這些狀態(tài)管理庫(kù)就顯得比較大材小用了,那么在不使用這些庫(kù)的情況下,如何去完成數(shù)據(jù)管理呢?比如面試最常問(wèn)的祖孫組件通信。在Vue
中我們可以使用provide/inject
,在React
中我們可以使用Context
。
假設(shè)有這樣一個(gè)場(chǎng)景,系統(tǒng)現(xiàn)在需要提供一個(gè)換膚功能,用戶(hù)可以切換皮膚,現(xiàn)在我們分別使用Vue
和React
來(lái)實(shí)現(xiàn)這個(gè)功能。
Vue
中的provide/inject
在Vue
中我們可以使用provide/inject
來(lái)實(shí)現(xiàn)跨多級(jí)組件進(jìn)行傳值,就以上面所說(shuō)場(chǎng)景為例,我們使用provide/inject
來(lái)實(shí)現(xiàn)以下
首先,修改App.vue
內(nèi)容為以下內(nèi)容
<div id="app">
</div>
export default {
data() {
return {
themeInfo: {
theme: 'dark'
}
}
},
provide() {
return {
theme: this.themeInfo
}
}
}
然后在任意層級(jí)的子組件中像下面這樣使用
<div :class="`child-${theme.theme}`">
</div>
export default {
inject: ['theme']
}
這樣就可以實(shí)現(xiàn)theme
在所有子組件中進(jìn)行共享了
React
中的Context
在Vue
中我們使用provide/inject
實(shí)現(xiàn)了組件跨層級(jí)傳值功能,在React
中也提供了類(lèi)似的功能即Context
,下面我們使用Context
來(lái)實(shí)現(xiàn)相同的功能。
在項(xiàng)目src
目錄下新建context
目錄,添加MyContext.js
文件,然后添加以下內(nèi)容
import {createContext} from 'react'
// 定義 MyContext,指定默認(rèn)的主題為`light`
export const MyContext = createContext({
theme: 'light'
})
MyContext
提供了一個(gè)Provider
,通過(guò)Provider
可以將theme
共享到所有的子組件?,F(xiàn)在我們?cè)谒械慕M件的共同父組件比如App.js
上面添加MyContext.Provider
將theme
共享出去
import { MyContext } from '@/context/MyContext';
export default function() {
const [theme, setTheme] = useState('dark')
return (
)
}
然后這時(shí)候就可以直接在所有的子組件里面使用定義的主題theme
了
import React, { useContext } from 'react'
import { MyContext } from '@/context/MyContext';
export default function() {
const {theme} = useContext(MyContext)
return <div className={`child-${theme}`}>
}
沒(méi)有了v-model
,但也不影響使用
我們知道React
和Vue
都是單向數(shù)據(jù)流的,即數(shù)據(jù)的流向都是由外層向內(nèi)層組件進(jìn)行傳遞和更新的,比如下面這段React
代碼就是標(biāo)準(zhǔn)的單向數(shù)據(jù)流.
import React, { useState } from "react";
export default function(){
const [name] = useState('子君')
return <input value={name}></input>
}
在vue
中使用v-model
如上代碼,我們?cè)谕ㄟ^(guò)通過(guò)value
屬性將外部的值傳遞給了input
組件,這個(gè)就是一個(gè)簡(jiǎn)單的單向數(shù)據(jù)流。但是在使用Vue
的時(shí)候,還有兩個(gè)比較特殊的語(yǔ)法糖v-model
和.sync
,這兩個(gè)語(yǔ)法糖可以讓Vue
組件擁有雙向數(shù)據(jù)綁定的能力,比如下面的代碼
<input v-model="name"/>
export default {
data() {
return {
name:'子君'
}
}
}
通過(guò)v-model
,當(dāng)用戶(hù)修改input
的值的時(shí)候,外部的name
的值也將同步被修改。但這是Vue
的語(yǔ)法糖啊,React
是不支持的,所以React
應(yīng)該怎么辦呢?這時(shí)候再想想自定義v-model
,v-model
實(shí)際上是通過(guò)定義value
屬性同時(shí)監(jiān)聽(tīng)input
事件來(lái)實(shí)現(xiàn)的,比如這樣:
<div class="custom-input">
<input :value="value" @input="$_handleChange"/>
</div>
export default {
props:{
value:{
type: String,
default: ''
}
},
methods:{
$_handleChange(e) {
this.$emit('input', e.target.value)
}
}
}
在react
尋找v-model
替代方案
同理,React
雖然沒(méi)有v-model
語(yǔ)法糖,但是也可以通過(guò)傳入屬性然后監(jiān)聽(tīng)事件來(lái)實(shí)現(xiàn)數(shù)據(jù)的雙向綁定。
import React, { useState } from 'react'
export default function() {
const [name, setName] = useState('子君')
const handleChange = (e) => {
setName(e.target.value)
}
return <div>
<input value={name} onChange={handleChange}></input>
</div>
}
小編剛開(kāi)始使用react
,感覺(jué)沒(méi)有v-model
就顯得比較麻煩,不過(guò)麻煩歸麻煩,代碼改寫(xiě)也要寫(xiě)。就像上文代碼一樣,每一個(gè)表單元素都需要監(jiān)聽(tīng)onChange
事件,越發(fā)顯得麻煩了,這時(shí)候就可以考慮將多個(gè)onChange
事件合并成一個(gè),比如像下面代碼這樣
import React, { useState } from 'react'
export default function () {
const [name, setName] = useState('子君')
const [sex, setSex] = useState('男')
const handleChange = (e:any, method: Function) => {
method(e.target.value)
}
return <div>
<input value={name} onChange={(e) => handleChange(e, setName)}></input>
<input value={sex} onChange={(e) => handleChange(e, setSex)}></input>
</div>
}
沒(méi)有了指令,我感覺(jué)好迷茫
在Vue
中我們一般繪制頁(yè)面都會(huì)使用到template
,template
里面提供了大量的指令幫助我們完成業(yè)務(wù)開(kāi)發(fā),但是在React
中使用的是JSX
,并沒(méi)有指令,那么我們應(yīng)該怎么做呢?下面我們就將Vue
中最常用的一些指令轉(zhuǎn)換為JSX
里面的語(yǔ)法(注意: 在Vue中也可以使用JSX
)
v-show
與v-if
在Vue
中我們隱藏顯示元素可以使用v-show
或者v-if
,當(dāng)然這兩者的使用場(chǎng)景是有所不同的,v-show
是通過(guò)設(shè)置元素的display
樣式來(lái)顯示隱藏元素的,而v-if
隱藏元素是直接將元素從dom
中移除掉。
- 看一下
Vue
中的v-show
與v-if
的用法
<div>
<span v-show="showName">姓名:{{ name }}</span>
<span v-if="showDept">{{ dept }}</span>
</div>
export default {
data() {
return {
name: '子君',
dept: '銀河帝國(guó)',
showName: false,
showDept: true
}
}
}
- 將
v-show
,v-if
轉(zhuǎn)換為JSX
中的語(yǔ)法
在Vue
中指令是為了在template
方便動(dòng)態(tài)操作數(shù)據(jù)而存在的,但是到了React
中我們寫(xiě)的是JSX
,可以直接使用JS
,所以指令是不需要存在的,那么上面的v-show
,v-if
如何在JSX
中替代呢
import React, { useState } from 'react'
export default function() {
const [showName] = useState(false)
const [showDept] = useState(true)
const [userInfo] = useState({
name:'子君',
dept: '銀河帝國(guó)'
})
return (
<div>
{/**模擬 v-show */}
<span style={{display: showName ? 'block' : 'none'}}>{userInfo.name}</span>
{/**模擬 v-show */}
{showDept ? <span>{userInfo.dept}</span>: undefined}
</div>
)
}
v-for
v-for
在Vue
中是用來(lái)遍歷數(shù)據(jù)的,同時(shí)我們?cè)谑褂?code>v-for的時(shí)候需要給元素指定key
,key
的值一般是數(shù)據(jù)的id
或者其他唯一且固定的值。不僅在Vue
中,在React
中也是存在key
的,兩者的key
存在的意義基本一致,都是為了優(yōu)化虛擬DOM
diff
算法而存在的。
- 在
Vue
中使用v-for
<div>
<ul>
<li v-for="item in list" :key="item.id">
{{ item.name }}
</li>
</ul>
</div>
export default {
data() {
return {
list: [
{
id: 1,
name: '子君'
},
{
id: '2',
name: '張三'
},
{
id: '3',
name: '李四'
}
]
}
}
}
- 在
React
中使用v-for
的替代語(yǔ)法
在react
中雖然沒(méi)有v-for
,但是JSX
中可以直接使用JS
,所以我們可以直接遍歷數(shù)組
import React from 'react'
export default function() {
const data = [
{
id: 1,
name: "子君",
},
{
id: "2",
name: "張三",
},
{
id: "3",
name: "李四",
},
];
return (
<div>
<ul>
{
data.map(item => {
return <li key={item.id}>{item.name}</li>
})
}
</ul>
</div>
)
}
v-bind
與v-on
v-bind
在Vue
中是動(dòng)態(tài)綁定屬性的,v-on
是用于監(jiān)聽(tīng)事件的,因?yàn)?code>React也有屬性和事件的概念,所以我們?cè)?code>React也能發(fā)現(xiàn)可替代的方式。
- 在
Vue
中使用v-bind
與v-on
<div>
<input :value="value" @input="handleInput" />
</div>
export default {
data() {
return {
value: '子君'
}
},
methods: {
handleInput(e) {
this.value = e.target.value
}
}
}
- 在
React
中尋找替代方案
在Vue
中,作者將事件和屬性進(jìn)行了分離,但是在React
中,其實(shí)事件也是屬性,所以在本小節(jié)我們不僅看一下如何使用屬性和事件,再了解一下如何在React
中自定義事件
開(kāi)發(fā)一個(gè)CustomInput
組件
import React from 'react'
export interface CustomInputProps {
value: string;
//可以看出 onChange是一個(gè)普通的函數(shù),也被定義到了組件的props里面了
onChange: ((value: string,event: React.ChangeEvent) => void) | undefined;
}
export default function(props: CustomInputProps) {
function handleChange(e: React.ChangeEvent) {
// props.onChange是一個(gè)屬性,也是自定義的一個(gè)事件
props.onChange && props.onChange(e.target.value, e)
}
return (
<input value={props.value} onChange={handleChange}></input>
)
}
使用CustomInput
組件
import React, { useState } from 'react'
import CustomInput from './components/CustomInput'
export default function() {
const [value, setValue] = useState('')
function handleChange(value: string) {
setValue(value)
}
return (
<div>
</div>
)
}
總結(jié)
剛開(kāi)始從Vue
轉(zhuǎn)到 React 的時(shí)候,其實(shí)是有點(diǎn)不適應(yīng)的,但是當(dāng)慢慢的習(xí)慣之后,就會(huì)發(fā)現(xiàn)Vue
和 React 是存在很多共性的,可以參考的去學(xué)習(xí)。當(dāng)然無(wú)論Vue
還是 React ,上手比較快,但是想深入學(xué)習(xí)還是需要下功夫的,后續(xù)小編將會(huì)對(duì)Vue
和 React 的用法在做更深入的介紹,敬請(qǐng)期待。
文章來(lái)源于公眾號(hào):前端進(jìn)擊者,作者:前端有的玩
以上就是W3Cschool編程獅
關(guān)于適合Vue用戶(hù)的React教程,走過(guò)路過(guò)千萬(wàn)不要錯(cuò)過(guò)的相關(guān)介紹了,希望對(duì)大家有所幫助。