適合Vue用戶的React教程,你值得擁有

雙節旅遊人如山,不如家中代碼閒。
學以至用加班少,王者榮耀家中玩。

小編平常工做中使用的是Vue,對於React只是作過簡單的瞭解,並無作過深刻學習。趁着這個雙節假期,小編決定好好學一學React,今天這篇文章就是小編在學習React以後,將ReactVue的用法作的一個對比,經過這個對比,方便使用Vue的小夥伴能夠快速將Vue中的寫法轉換爲React的寫法。javascript

本文首發於公衆號【前端有的玩】,玩前端,面試找工做,就在【前端有的玩】,歡迎關注前端

插槽,在React中沒找到??

在使用Vue的時候,插槽是一個特別經常使用的功能,經過定義插槽,能夠在調用組件的時候將外部的內容傳入到組件內部,顯示到指定的位置。在Vue中,插槽分爲默認插槽,具名插槽和做用域插槽。其實不只僅Vue,在React中其實也有相似插槽的功能,只是名字不叫作插槽,下面我將經過舉例來講明。vue

默認插槽

如今項目須要開發一個卡片組件,以下圖所示,卡片能夠指定標題,而後卡片內容能夠用戶自定義,這時候對於卡片內容來講,就可使用插槽來實現,下面咱們就分別使用VueReact來實現這個功能java

Vue實現

  1. 首先實現一個card組件,以下代碼所示react

    <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>

    如上代碼,就可使用組件的默認插槽將外部的內容應用到組件裏面指定的位置了。redux

React實現

雖然在React裏面沒有插槽的概念,可是React裏面也能夠經過props.children拿到組件標籤內部的子元素的,就像上面代碼<my-card>標籤內的子元素,經過這個咱們也能夠實現相似Vue默認插槽的功能,一塊兒看看代碼。數組

  1. 使用React定義Card組件app

    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的用法在作更深刻的介紹,敬請期待。

相關文章
相關標籤/搜索