React、Vue我全都要!React Hook 實現 Vue 的11個基本功能

Sunshine_Lin發表於2021-12-03

前言

大家好,我是林三心,因為工作專案的需要,在上個月,我開始在專案中使用React去開發,到了今天,差不多一個月了,想跟大家分享一下我在React中是怎麼去實現以前Vue中的一些功能的。

由於本菜鳥使用React不久,有不妥之處還請大家之處

註明:本文中所指 Vue 的版本是Vue2, React 的版本是 React17

1、JSX和template

在Vue2中是使用 template 的,這點使用 Vue 的同學們都知道,而在 React 中使用的是 JSX JSX 是一個看起來很像 XML 的 JavaScript 語法擴充套件。

它有以下優點:

  • JSX 執行更快,因為它在編譯為 JavaScript 程式碼後進行了優化。
  • 它是型別安全的,在編譯過程中就能發現錯誤。
  • 使用 JSX 編寫模板更加簡單快速。

JSX 的例子:使用ReactDOM.render函式,將DOM渲染到對應到id為app的節點下

// 使用ReactDOM.render函式,將DOM渲染到對應到id為app的節點下
ReactDOM.render(
  <div>
      <h1>我是林三心</h1>
      <h2>我是菜鳥</h2>
      <p>React是一個很不錯的 JavaScript 庫!</p>
  </div>
  ,
  document.getElementById('app')
);

2、React 中給元素設定 style

React 使用內聯樣式。我們可以使用 駝峰法 語法來設定內聯樣式. React 會在指定元素數字後自動新增 px 。以下例項演示了為 h1 元素新增 myStyle 內聯樣式:

function Demo() {

  var myStyle = {
    fontSize: 100, // 駝峰法
    color: '#FF0000'
  }

  return <h1 style={myStyle}>林三心是菜鳥</h1>
}

3、React 中給元素設定 class

由於 JSX 就是 JavaScript,一些識別符號像 class 不建議作為 XML 屬性名。作為替代,使用 className來做對應的屬性。

function Demo() {

  const classes = 'haha heihei xixi'

  return (
    <div>
      <h1 className='haha'>林三心是菜鳥</h1> // 單個
      <h1 className='haha heihei'>林三心是菜鳥</h1> // 多個
      <h1 className={classes}>林三心是菜鳥</h1> // 使用值來當做class
    </div>
  )
}

4、React 中的點選事件

Vue 中的點選事件使用的是 @click 來觸發的,而在 JSX 中使用的是 onClick

function Demo() {
  const handleClick = () => {
    console.log('林三心是菜鳥')
  }

  return (
    <button onClick={handleClick}>點我</button>
  )
}

5、React 中修改值觸發DOM更新

我使用的是 React hook 其中的 useState ,這一個hook在修改常量的時候比較簡單,但是在修改引用 物件 或者 陣列 的時候就需要先進行 淺拷貝 再進行覆蓋修改

import { useState } from 'react'

function Demo() {
  const [msg, setMsg] = useState('我是菜鳥')
  const [obj, setObj] = useState({
    name: '林三心',
    food: '泡麵'
  })
  const [arr, setArr] = useState([
    { message: '林三心啊', id: 1 },
    { message: '林三心啊啊', id: 2 },
    { message: '林三心啊啊啊', id: 3 }
  ])
  const handleClick = (type: number) => {
    switch (type) {
      case 1:
        setMsg('林三心是菜鳥') // 直接賦值
        break;
      case 2:
        setObj({ ...obj, food: '牛肉丸', }) // 淺拷貝
        break;
      case 3:
        setArr([...arr, { message: '林三心啊啊啊啊', id: 4}]) // 淺拷貝實現push效果
        break;
    }
  }

  return (
    <div>
      <button onClick={() => handleClick(1)}>修改msg</button>
      <button onClick={() => handleClick(2)}>修改obj的food</button>
      <button onClick={() => handleClick(3)}>arr新增一項</button>
      <h1>{msg}</h1>
      <p>{`我是${obj.name}我喜歡吃${obj.food}`}</p>
      <ul>
        {
          arr.map(({ message, id }) => {
            return <li key={id}>{message}</li>
          })
        }
      </ul >
    </div>
  )
}

6、生命週期

使用React的hook—— useEffect

import { useState, useEffect } from 'react'
function App() {
  const [num, setNum] = useState(1)
  const [count, setCount] = useState(1)
  useEffect(() => {
    console.log('哈哈哈哈')
  })
  return (
    <div>
      <button onClick={() => setNum(num + 1)}>點我修改num</button>
      <button onClick={() => setCount(count + 1)}>點我count</button>
    </div>
  )
}

第二個引數不傳

 useEffect(() => {
    console.log('哈哈哈哈')
 })

useEffect 第二個引數不傳時, 頁面初始 資料更新 的時候,第一個引數函式都會執行,所以此時初始頁面時會輸出一次 哈哈哈哈 ,然後無論你點修改num或者修改count的按鈕時,也都會輸出 哈哈哈哈

第二個引數傳空陣列

 useEffect(() => {
    console.log('哈哈哈哈')
 }, [])

useEffect 第二個引數傳 [] 時,那麼第一個引數函式只有在 頁面初始 的時候才會執行,也就是隻執行一次,無論你點修改num或者修改count的按鈕,都不會執行這個函式

第二個引數傳非空陣列

 // ①
 useEffect(() => {
    console.log('哈哈哈哈')
 }, [num])
 
 // ②
 useEffect(() => {
    console.log('哈哈哈哈')
 }, [count])
 
 // ③
 useEffect(() => {
    console.log('哈哈哈哈')
 }, [num, count])

useEffect 第二個引數傳非空陣列時, 頁面初始 依賴的資料發生更新 的時候,第一個引數函式都會執行。比如上方例子:

  • ①、只有按修改num按鈕時,才會再次輸出 哈哈哈哈
  • ②、只有按修改count按鈕時,才會再次輸出 哈哈哈哈
  • ③、無論按哪個按鈕都會再次輸出 哈哈哈哈

return清除操作

useEffect(() => {
    const timeId = setTimeout(() => console.log('我是定時器'), 1000)
    return () => clearTimeout(timeId)
 })

React 會在元件解除安裝的時候執行清除操作。effect 在每次渲染的時候都會執行。React 會在執行當前 effect 之前對上一個 effect 進行清除。

是在還不理解的同學,可以瘋狂點選按鈕,看看 我是定時器 這句話會輸出多遍還是隻輸出一遍,就恍然大悟了

7、React 中實現 v-if & v-else

Vue 中的 v-if & v-else

v-if 指令用於條件性地渲染一塊內容。這塊內容只會在指令的表示式返回 true 值的時候被渲染。

<h1 v-if="show">林三心是菜鳥</h1>

也可以用 v-else 新增一個“else 塊”:

<h1 v-if="show">林三心是菜鳥</h1>
<h1 v-else>Oh no ?</h1>

React中實現

如果單單隻想實現 v-if 的話,可以藉助 && 邏輯運算子

import { useState } from 'react'
function Demo() {

  const [show, setShow] = useState(false)
  const changeShow = () => {
    setShow(!show)
  }

  return (
    <div>
      {show && <h1>林三心是菜鳥</h1>}
      <button onClick={changeShow}>點我</button>
    </div>
  )
}

如果想實現 v-if v-else 的話,可以藉助 三元運算子

import { useState } from 'react'
function Demo() {

  const [show, setShow] = useState(false)
  const changeShow = () => {
    setShow(!show)
  }

  return (
    <div>
      {show ? <h1>林三心是菜鳥</h1> : <h1>菜鳥是林三心</h1>}
      <button onClick={changeShow}>點我</button>
    </div>
  )
}

8、React 中實現 v-show

Vue 中的 v-show

另一個用於根據條件展示元素的選項是 v-show 指令。用法大致一樣:

<h1 v-show="show">林三心是菜鳥</h1>

不同的是帶有 v-show 的元素始終會被渲染並保留在 DOM 中。v-show 只是簡單地切換元素的 CSS property display

React中實現

其實就是改變元素的 display 這個樣式來實現效果

function Demo() {

  // ...一樣的程式碼
  
  return (
    <div>
      <h1 style={{display: show ? 'block': 'none'}}>林三心是菜鳥</h1>
      <button onClick={changeShow}>點我</button>
    </div>
  )
}

9、React 中實現 v-for

我們可以用 v-for 指令基於一個陣列來渲染一個列表。v-for 指令需要使用 item in items 形式的特殊語法,其中 items 是源資料陣列,而 item 則是被迭代的陣列元素的別名

Vue 中的 v-for

<ul>
  <li v-for="item in items" :key="item.message">
    {{ item.message }}
  </li>
</ul>

React中實現

JSX 允許在模板中插入陣列,陣列會自動展開所有成員:

function Demo() {

  const arr = [
    <li key={1}>林三心啊</li>,
    <li key={2}>林三心啊啊</li>,
    <li key={3}>林三心啊啊啊</li>,
  ]

  return (
    <ul>
      {arr}
    </ul >
  )
}

但是我大多數情況會使用陣列的 map 方法來協助渲染

function Demo() {

  const arr = [
    { message: '林三心啊', id: 1 },
    { message: '林三心啊啊', id: 2 },
    { message: '林三心啊啊啊', id: 3 }
  ]

  return (
    <ul>
      {
        arr.map(({ message, id }) => {
          return <li key={id}>{message}</li>
        })
      }
    </ul >
  )
}

10、React 中實現 computed

Vue 中的 computed

只要 name 或者 food 改變, mag 會更新成相應的值

<h1>{{msg}}</h1>

computed: { msg() { return `我是${this.name},我愛吃${this.food}` } }

React中實現

在 React 中需要通過 useMemo 這個 hook 來來實現 computed 的效果

import { useState, useMemo } from 'react'
function Demo() {
  const [name, setName] = useState('林三心')
  const [food, setFood] = useState('泡麵')

  // 實現computed的功能
  const msg = useMemo(() => `我是${name},我愛吃${food}`, [name, food]) // 監聽name和food這兩個變數

  const handleClick = (type: number) => {
    if (type === 1) {
      setName('大菜鳥')
    } else if (type === 2) {
      setFood('牛肉丸')
    }
  }

  return (
    <div>
      <button onClick={() => handleClick(1)}>修改name</button>
      <button onClick={() => handleClick(2)}>修改food</button>
      <h1>{msg}</h1>
    </div>
  )
}

11、React 中實現 watch

// useWatch.ts
import { useEffect, useRef } from 'react'

type Callback<T> = (prev?: T) => void
interface Config {
  immdiate: Boolean
}

const useWatch = <T>(data: T, callback: Callback<T>, config: Config = { immdiate: false }) => {
  const prev = useRef<T>()
  const { immdiate } = config
  const inited = useRef(false)
  const stop = useRef(false)
  useEffect(() => {
    const execute = () => callback(prev.current)
    if (!stop.current) {
      if (!inited.current) {
        inited.current = true
        immdiate && execute()
      } else {
        execute()
      }
      prev.current = data
    }
  }, [data])

  return () => stop.current = true
}

export default useWatch

使用

import { useState } from 'react'
import useWatch from '/@/hooks/web/useWatch'
function App() {
  const [num, setNum] = useState(1)
  useWatch(num, (pre) => console.log(pre, num), { immdiate: true })
  return (
    <div>
      <div style={{ color: '#fff' }}>{num}</div>
      <button onClick={() => setNum(num + 1)}>點我</button>
    </div>
  )
}

結語

今年快結束了,希望大家身體健康,萬事如意

我是林三心,一個熱心的前端菜鳥程式設計師。如果你上進,喜歡前端,想學習前端,那我們們可以交朋友,一起摸魚哈哈,或者你有合適的前端崗位機會,可以讓我試試的,那可以加我的wx --> meron857287645

相關文章