双节游览人如山,不如家中代码闲。
学以致用加班少,王者光荣家中玩。
小编日常工作中应用的是Vue
,对于React
只是做过简略的理解,并没有做过深刻学习。趁着这个双节假期,小编决定好好学一学React
,明天这篇文章就是小编在学习React
之后,将React
与Vue
的用法做的一个比照,通过这个比照,方便使用Vue
的小伙伴能够疾速将Vue
中的写法转换为React
的写法。
本文首发于公众号【前端有的玩】,玩前端,面试找工作,就在【前端有的玩】,欢送关注
插槽,在React
中没找到??
在应用Vue
的时候,插槽是一个特地罕用的性能,通过定义插槽,能够在调用组件的时候将内部的内容传入到组件外部,显示到指定的地位。在Vue
中,插槽分为默认插槽,具名插槽和作用域插槽。其实不仅仅Vue
,在React
中其实也有相似插槽的性能,只是名字不叫做插槽,上面我将通过举例来说明。
默认插槽
当初我的项目须要开发一个卡片组件,如下图所示,卡片能够指定题目,而后卡片内容能够用户自定义,这时候对于卡片内容来说,就能够应用插槽来实现,上面咱们就别离应用Vue
和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
组件<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
默认插槽的性能,一起看看代码。
应用
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实现
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
连插槽都没有, 更别提具名插槽了,然而没有不代表不能模仿进去。对于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
的用法在做更深刻的介绍,敬请期待。