双节游览人如山,不如家中代码闲。
学以致用加班少,王者光荣家中玩。
小编日常工作中应用的是 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
的用法在做更深刻的介绍,敬请期待。