乐趣区

关于javascript:适合Vue用户的React教程你值得拥有

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

小编日常工作中应用的是 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的用法在做更深刻的介绍,敬请期待。

退出移动版