双节游览人如山,不如家中代码闲。
学以致用加班少,王者光荣家中玩。

小编日常工作中应用的是Vue,对于React只是做过简略的理解,并没有做过深刻学习。趁着这个双节假期,小编决定好好学一学React,明天这篇文章就是小编在学习React之后,将ReactVue的用法做的一个比照,通过这个比照,方便使用Vue的小伙伴能够疾速将Vue中的写法转换为React的写法。

本文首发于公众号【前端有的玩】,玩前端,面试找工作,就在【前端有的玩】,欢送关注

插槽,在React中没找到??

在应用Vue的时候,插槽是一个特地罕用的性能,通过定义插槽,能够在调用组件的时候将内部的内容传入到组件外部,显示到指定的地位。在Vue中,插槽分为默认插槽,具名插槽和作用域插槽。其实不仅仅Vue,在React中其实也有相似插槽的性能,只是名字不叫做插槽,上面我将通过举例来说明。

默认插槽

当初我的项目须要开发一个卡片组件,如下图所示,卡片能够指定题目,而后卡片内容能够用户自定义,这时候对于卡片内容来说,就能够应用插槽来实现,上面咱们就别离应用VueReact来实现这个性能

Vue实现

  1. 首先实现一个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>所在位置

  2. 在内部应用定义的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默认插槽的性能,一起看看代码。

  1. 应用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>  );}
    1. 在内部应用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属性来实现了。

  1. 咱们就下面的需要对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>
  1. 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连插槽都没有, 更别提具名插槽了,然而没有不代表不能模仿进去。对于Reactprops,咱们不仅仅能够传入一般的属性,还能够传入一个函数,这时候咱们就能够在传入的这个函数外面返回JSX,从而就实现了具名插槽的性能。

  1. 对原有的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>  );}
  1. 这时候就能够在内部自定义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实现
  1. 实现用户信息卡片组件,外面应用了作用域插槽
<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>
  1. 在内部应用人员信息组件
<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的形式来模仿了具名插槽,那么对于作用域插槽,咱们仍然能够应用函数的这种形式,而作用域插槽传递的参数咱们能够应用给函数传参的形式来代替

  1. 实现人员信息卡片组件

    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>}
  2. 在内部应用人员信息卡片组件

    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

假如有这样一个场景,零碎当初须要提供一个换肤性能,用户能够切换皮肤,当初咱们别离应用VueReact来实现这个性能。

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.Providertheme共享进来

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,但也不影响应用

咱们晓得ReactVue都是单向数据流的,即数据的流向都是由外层向内层组件进行传递和更新的,比方上面这段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-modelv-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中咱们个别绘制页面都会应用到templatetemplate外面提供了大量的指令帮忙咱们实现业务开发,然而在React中应用的是JSX,并没有指令,那么咱们应该怎么做呢?上面咱们就将Vue中最罕用的一些指令转换为JSX外面的语法(留神: 在Vue中也能够应用JSX)

v-showv-if

Vue中咱们暗藏显示元素能够应用v-show或者v-if,当然这两者的应用场景是有所不同的,v-show是通过设置元素的display款式来显示暗藏元素的,而v-if暗藏元素是间接将元素从dom中移除掉。

  1. 看一下Vue中的v-showv-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>
  2. v-showv-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-forVue中是用来遍历数据的,同时咱们在应用v-for的时候须要给元素指定keykey的值个别是数据的id或者其余惟一且固定的值。不仅在Vue中,在React中也是存在key的,两者的key存在的意义基本一致,都是为了优化虚构DOM diff算法而存在的。

  1. 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>
  2. 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-bindv-on

v-bindVue中是动静绑定属性的,v-on是用于监听事件的,因为React也有属性和事件的概念,所以咱们在React也能发现可代替的形式。

  1. Vue中应用v-bindv-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>
  2. 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的时候,其实是有点不适应的,然而当缓缓的习惯之后,就会发现VueReact是存在很多共性的,能够参考的去学习。当然无论Vue还是React,上手比拟快,然而想深刻学习还是须要下功夫的,后续小编将会对VueReact的用法在做更深刻的介绍,敬请期待。