双节旅游人如山,不如家中代码闲。 学以至用加班少,王者荣耀家中玩。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
的用法在作更深刻的介绍,敬请期待。