雙節旅遊人如山,不如家中代碼閒。 學以至用加班少,王者榮耀家中玩。javascript
小編平常工做中使用的是Vue
,對於React
只是作過簡單的瞭解,並無作過深刻學習。趁着這個雙節假期,小編決定好好學一學React
,今天這篇文章就是小編在學習React
以後,將React
與Vue
的用法作的一個對比,經過這個對比,方便使用Vue
的小夥伴能夠快速將Vue
中的寫法轉換爲React
的寫法。前端
本文首發於公衆號【前端有的玩】,玩前端,面試找工做,就在【前端有的玩】vue
React
中沒找到??在使用Vue
的時候,插槽是一個特別經常使用的功能,經過定義插槽,能夠在調用組件的時候將外部的內容傳入到組件內部,顯示到指定的位置。在Vue
中,插槽分爲默認插槽,具名插槽和做用域插槽。其實不只僅Vue
,在React
中其實也有相似插槽的功能,只是名字不叫作插槽,下面我將經過舉例來講明。java
如今項目須要開發一個卡片組件,以下圖所示,卡片能夠指定標題,而後卡片內容能夠用戶自定義,這時候對於卡片內容來講,就可使用插槽來實現,下面咱們就分別使用Vue
和React
來實現這個功能react
Vue
實現首先實現一個card
組件,以下代碼所示面試
<template>
<div class="card">
<div class="card__title">
<span>{{ title }}</span>
</div>
<div class="card__body">
<slot></slot>
</div>
</div>
</template>
<script>
export default {
props: {
title: {
type: String,
default: ''
}
}
}
</script>
複製代碼
能夠看到上面咱們使用了<slot></slot>
,這個就是組件的默認插槽,在使用組件的時候,傳入的內容將會被放到<slot></slot>
所在位置算法
在外部使用定義的card
組件redux
<template>
<div>
<my-card>
<div>我將被放在card組件的默認插槽裏面</div>
</my-card>
</div>
</template>
<script>
import MyCard from '../components/card'
export default {
components: {
MyCard
}
}
</script>
複製代碼
如上代碼,就可使用組件的默認插槽將外部的內容應用到組件裏面指定的位置了。數組
React
實現雖然在React
裏面沒有插槽的概念,可是React
裏面也能夠經過props.children
拿到組件標籤內部的子元素的,就像上面代碼<my-card>
標籤內的子元素,經過這個咱們也能夠實現相似Vue
默認插槽的功能,一塊兒看看代碼。markdown
使用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">
{/**每一個組件均可以獲取到 props.children。它包含組件的開始標籤和結束標籤之間的內容 */}
{props.children}
</div>
</div>
);
}
複製代碼
Card
組件import React from 'react'
import Card from './components/Card'
export default function () {
return (
<div>
<Card title="標題">
<div>我將被放在card組件的body區域內容</div>
</Card>
</div>
);
}
複製代碼
繼續以上面的Card
組件爲例,假如咱們如今需求發生了變化,組件的title
也可使用插槽,這時候對於Vue
就可使用具名插槽了,而React
也是有辦法實現的哦。
Vue
的具名插槽主要解決的是一個組件須要多個插槽的場景,其實現是爲<slot>
添加name
屬性來實現了。
card
組件進行修改<template>
<div class="card">
<div class="card__title">
<!--若是傳入了title,則使用title屬性,不然使用具名插槽-->
<span v-if="title">{{ title }}</span>
<slot v-else name="title"></slot>
</div>
<div class="card__body">
<!--對於內容區域依然使用默認插槽-->
<slot></slot>
</div>
</div>
</template>
<script>
export default {
props: {
title: {
type: String,
default: ''
}
}
}
</script>
複製代碼
card
組件修改完以後,咱們再去調整一下使用card
組件的地方<template>
<div>
<my-card>
<!--經過v-slot:title 使用具名插槽-->
<template v-slot:title>
<span>這裏是標題</span>
</template>
<div>我將被放在card組件的默認插槽裏面</div>
</my-card>
</div>
</template>
<script>
import MyCard from '../components/card'
export default {
components: {
MyCard
}
}
</script>
複製代碼
React
連插槽都沒有, 更別提具名插槽了,可是沒有不表明不能模擬出來。對於React
的props
,咱們不只僅能夠傳入普通的屬性,還能夠傳入一個函數,這時候咱們就能夠在傳入的這個函數裏面返回JSX
,從而就實現了具名插槽的功能。
Card
組件進行修改import React from 'react'
export interface CardProps {
title?: string,
// 加入了一個renderTitle屬性,屬性類型是Function
renderTitle?: Function,
children: React.ReactNode
}
export default function(props: CardProps) {
const {title, renderTitle} = props
// 若是指定了renderTtile,則使用renderTitle,不然使用默認的title
let titleEl = renderTitle ? renderTitle() : <span>{title}</span>
return (
<div className="card">
<div className="card__title">{titleEl}</div>
<div className="card__body">
{/**每一個組件均可以獲取到 props.children。它包含組件的開始標籤和結束標籤之間的內容 */}
{props.children}
</div>
</div>
);
}
複製代碼
title
了import React from 'react'
import Card from './components/Card'
export default function () {
return (
<div>
<Card renderTitle={
() => {
return <span>我是自定義的標題</span>
}
}>
<div>我將被放在card組件的body區域內容</div>
</Card>
</div>
);
}
複製代碼
有時讓插槽內容可以訪問子組件中才有的數據是頗有用的,這個就是Vue
提供做用域插槽的緣由。咱們繼續使用上面的Card
組件爲例,如今我基於上面的卡片組件開發了一我的員信息卡片組件,用戶直接使用人員信息卡片組件就能夠將人員信息顯示到界面中,可是在某些業務模塊須要自定義人員信息顯示方式,這時候咱們就須要使用到做用域插槽了。
Vue
實現<template>
<custom-card title="人員信息卡片">
<div class="content">
<!--這裏使用了做用域插槽,將userInfo傳出去了-->
<slot name="userInfo" :userInfo="userInfo">
<!--若是沒有使用插槽,則顯示默認內容-->
<span>姓名: {{ userInfo.name }}</span>
<span>性別: {{ userInfo.sex }}</span>
<span>年齡: {{ userInfo.age }}</span>
</slot>
</div>
</custom-card>
</template>
<script>
import CustomCard from '../card'
export default {
components: {
CustomCard
},
data() {
return {
userInfo: {
name: '張三',
sex: '男',
age: 25
}
}
}
}
</script>
複製代碼
<template>
<div>
<user-card>
<template v-slot:userInfo="{ userInfo }">
<div class="custom-user">
<ul>
<li>姓名: {{ userInfo.name }}</li>
<li>年齡: {{ userInfo.age }}</li>
</ul>
</div>
</template>
</user-card>
</div>
</template>
<script>
import UserCard from '../components/user-card'
export default {
components: {
UserCard
}
}
</script>
複製代碼
React
實現在具名插槽那一小節咱們經過給組件傳入了一個函數,而後在函數中返回JSX
的方式來模擬了具名插槽,那麼對於做用域插槽,咱們依然可使用函數的這種方式,而做用域插槽傳遞的參數咱們可使用給函數傳參的方式來替代
實現人員信息卡片組件
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<UserInfo>({
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 <Card title="人員信息">
{content}
</Card>
}
複製代碼
在外部使用人員信息卡片組件
import React from 'react'
import UserCard, { UserInfo } from "./components/UserCard";
export default function () {
return (
<div>
<UserCard
renderUserInfo={(userInfo: UserInfo) => {
return (
<ul>
<li>姓名: {userInfo.name}</li>
</ul>
);
}}
></UserCard>
</div>
);
}
複製代碼
Context
, React
中的provide/inject
一般咱們在項目開發中,對於多組件之間的狀態管理,在Vue
中會使用到Vuex
,在React
中會使用到redux
或者Mobx
,但對於小項目來講,使用這些狀態管理庫就顯得比較大材小用了,那麼在不使用這些庫的狀況下,如何去完成數據管理呢?好比面試最常問的祖孫組件通訊。在Vue
中咱們可使用provide/inject
,在React
中咱們可使用Context
。
假設有這樣一個場景,系統如今須要提供一個換膚功能,用戶能夠切換皮膚,如今咱們分別使用Vue
和React
來實現這個功能。
Vue
中的provide/inject
在Vue
中咱們可使用provide/inject
來實現跨多級組件進行傳值,就以上面所說場景爲例,咱們使用provide/inject
來實現如下
首先,修改App.vue
內容爲如下內容
<template>
<div id="app">
<router-view />
</div>
</template>
<script>
export default {
data() {
return {
themeInfo: {
theme: 'dark'
}
}
},
provide() {
return {
theme: this.themeInfo
}
}
}
</script>
複製代碼
而後在任意層級的子組件中像下面這樣使用
<template>
<div :class="`child-${theme.theme}`">
</div>
</template>
<script>
export default {
inject: ['theme']
}
</script>
複製代碼
這樣就能夠實現theme
在全部子組件中進行共享了
React
中的Context
在Vue
中咱們使用provide/inject
實現了組件跨層級傳值功能,在React
中也提供了相似的功能即Context
,下面咱們使用Context
來實現相同的功能。
在項目src
目錄下新建context
目錄,添加MyContext.js
文件,而後添加如下內容
import {createContext} from 'react'
// 定義 MyContext,指定默認的主題爲`light`
export const MyContext = createContext({
theme: 'light'
})
複製代碼
MyContext
提供了一個Provider
,經過Provider
能夠將theme
共享到全部的子組件。如今咱們在全部的組件的共同父組件好比App.js
上面添加MyContext.Provider
將theme
共享出去
import { MyContext } from '@/context/MyContext';
export default function() {
const [theme, setTheme] = useState('dark')
return (
<MyContext.Provider
value={{
theme
}}
>
<Children></Children>
</MyContext.Provider>
)
}
複製代碼
而後這時候就能夠直接在全部的子組件裏面使用定義的主題theme
了
import React, { useContext } from 'react'
import { MyContext } from '@/context/MyContext';
export default function() {
const {theme} = useContext(MyContext)
return <div className={`child-${theme}`}>
}
複製代碼
v-model
,但也不影響使用咱們知道React
和Vue
都是單向數據流的,即數據的流向都是由外層向內層組件進行傳遞和更新的,好比下面這段React
代碼就是標準的單向數據流.
import React, { useState } from "react";
export default function(){
const [name] = useState('子君')
return <input value={name}></input>
}
複製代碼
vue
中使用v-model
如上代碼,咱們在經過經過value
屬性將外部的值傳遞給了input
組件,這個就是一個簡單的單向數據流。可是在使用Vue
的時候,還有兩個比較特殊的語法糖v-model
和.sync
,這兩個語法糖可讓Vue
組件擁有雙向數據綁定的能力,好比下面的代碼
<template>
<input v-model="name"/>
</template>
<script>
export default {
data() {
return {
name:'子君'
}
}
}
</script>
複製代碼
經過v-model
,當用戶修改input
的值的時候,外部的name
的值也將同步被修改。但這是Vue
的語法糖啊,React
是不支持的,因此React
應該怎麼辦呢?這時候再想一想自定義v-model
,v-model
其實是經過定義value
屬性同時監聽input
事件來實現的,好比這樣:
<template>
<div class="custom-input">
<input :value="value" @input="$_handleChange"/>
</div>
</template>
<script>
export default {
props:{
value:{
type: String,
default: ''
}
},
methods:{
$_handleChange(e) {
this.$emit('input', e.target.value)
}
}
}
</script>
複製代碼
react
尋找v-model
替代方案同理,React
雖然沒有v-model
語法糖,可是也能夠經過傳入屬性而後監聽事件來實現數據的雙向綁定。
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>
}
複製代碼
小編剛開始使用react
,感受沒有v-model
就顯得比較麻煩,不過麻煩歸麻煩,代碼改寫也要寫。就像上文代碼同樣,每個表單元素都須要監聽onChange
事件,愈加顯得麻煩了,這時候就能夠考慮將多個onChange
事件合併成一個,好比像下面代碼這樣
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>
}
複製代碼
在Vue
中咱們通常繪製頁面都會使用到template
,template
裏面提供了大量的指令幫助咱們完成業務開發,可是在React
中使用的是JSX
,並無指令,那麼咱們應該怎麼作呢?下面咱們就將Vue
中最經常使用的一些指令轉換爲JSX
裏面的語法(注意: 在Vue中也可使用JSX
)
v-show
與v-if
在Vue
中咱們隱藏顯示元素可使用v-show
或者v-if
,固然這二者的使用場景是有所不一樣的,v-show
是經過設置元素的display
樣式來顯示隱藏元素的,而v-if
隱藏元素是直接將元素從dom
中移除掉。
看一下Vue
中的v-show
與v-if
的用法
<template>
<div>
<span v-show="showName">姓名:{{ name }}</span>
<span v-if="showDept">{{ dept }}</span>
</div>
</template>
<script>
export default {
data() {
return {
name: '子君',
dept: '銀河帝國',
showName: false,
showDept: true
}
}
}
</script>
複製代碼
將v-show
,v-if
轉換爲JSX
中的語法
在Vue
中指令是爲了在template
方便動態操做數據而存在的,可是到了React
中咱們寫的是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: '銀河帝國'
})
return (
<div>
{/**模擬 v-show */}
<span style={{display: showName ? 'block' : 'none'}}>{userInfo.name}</span>
{/**模擬 v-if */}
{showDept ? <span>{userInfo.dept}</span>: undefined}
</div>
)
}
複製代碼
v-for
v-for
在Vue
中是用來遍歷數據的,同時咱們在使用v-for
的時候須要給元素指定key
,key
的值通常是數據的id
或者其餘惟一且固定的值。不只在Vue
中,在React
中也是存在key
的,二者的key
存在的意義基本一致,都是爲了優化虛擬DOM
diff
算法而存在的。
在Vue
中使用v-for
<template>
<div>
<ul>
<li v-for="item in list" :key="item.id">
{{ item.name }}
</li>
</ul>
</div>
</template>
<script>
export default {
data() {
return {
list: [
{
id: 1,
name: '子君'
},
{
id: '2',
name: '張三'
},
{
id: '3',
name: '李四'
}
]
}
}
}
</script>
複製代碼
在React
中使用v-for
的替代語法
在react
中雖然沒有v-for
,可是JSX
中能夠直接使用JS
,因此咱們能夠直接遍歷數組
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
中是動態綁定屬性的,v-on
是用於監聽事件的,由於React
也有屬性和事件的概念,因此咱們在React
也能發現可替代的方式。
在Vue
中使用v-bind
與v-on
<template>
<div>
<!--:value是v-bind:value的簡寫, @input是v-on:input的簡寫-->
<input :value="value" @input="handleInput" />
</div>
</template>
<script>
export default {
data() {
return {
value: '子君'
}
},
methods: {
handleInput(e) {
this.value = e.target.value
}
}
}
</script>
複製代碼
在React
中尋找替代方案
在Vue
中,做者將事件和屬性進行了分離,可是在React
中,其實事件也是屬性,因此在本小節咱們不只看一下如何使用屬性和事件,再瞭解一下如何在React
中自定義事件
開發一個CustomInput
組件
import React from 'react'
export interface CustomInputProps {
value: string;
//能夠看出 onChange是一個普通的函數,也被定義到了組件的props裏面了
onChange: ((value: string,event: React.ChangeEvent<HTMLInputElement>) => void) | undefined;
}
export default function(props: CustomInputProps) {
function handleChange(e: React.ChangeEvent<HTMLInputElement>) {
// props.onChange是一個屬性,也是自定義的一個事件
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>
<CustomInput value={value} onChange={handleChange}></CustomInput>
</div>
)
}
複製代碼
剛開始從Vue
轉到React
的時候,實際上是有點不適應的,可是當慢慢的習慣以後,就會發現Vue
和React
是存在不少共性的,能夠參考的去學習。固然不管Vue
仍是React
,上手比較快,可是想深刻學習仍是須要下功夫的,後續小編將會對Vue
和React
的用法在作更深刻的介紹,敬請期待。