国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

全面了解 React 新功能: Suspense 和 Hooks

Baaaan / 1068人閱讀

摘要:他們的應(yīng)用是比較復(fù)雜的,組件樹也是非常龐大,假設(shè)有一千個組件要渲染,每個耗費(fèi)一千個就是由于是單線程的,這里都在努力的干活,一旦開始,中間就不會停。

悄悄的, React v16.7 發(fā)布了。 React v16.7: No, This Is Not The One With Hooks.

最近我也一直在關(guān)注這兩個功能,就花些時間就整理了一下資料, 在此分享給大家, 希望對大家有所幫助。

引子

為什么不推薦在 componentwillmount 里最獲取數(shù)據(jù)的操作呢?

這個問題被過問很多遍了, 前幾天又討論到這個問題, 就以這個作為切入點(diǎn)吧。

有些朋友可能會想, 數(shù)據(jù)早點(diǎn)獲取回來,頁面就能快點(diǎn)渲染出來呀, 提升用戶體驗(yàn), 何樂而為不為?

這個問題, 簡單回答起來就是, 因?yàn)槭强赡軙?b>調(diào)用多次。

要深入回答這個問題, 就不得不提到一個React 的核心概念: React Fiber.

一些必須要先了解的背景 React Fiber

React Fiber 是在 v16 的時候引入的一個全新架構(gòu), 旨在解決異步渲染問題。

新的架構(gòu)使得使得 React 用異步渲染成為可能,但要注意,這個改變只是讓異步渲染成為可能

但是React 卻并沒有在 v16 發(fā)布的時候立刻開啟,也就是說,React 在 v16 發(fā)布之后依然使用的是同步渲染

不過,雖然異步渲染沒有立刻采用,F(xiàn)iber 架構(gòu)還是打開了通向新世界的大門,React v16 一系列新功能幾乎都是基于 Fiber 架構(gòu)。

說到這, 也要說一下 同步渲染異步渲染.

同步渲染 和 異步渲染 同步渲染

我們都知道React 是facebook 推出的, 他們內(nèi)部也在大量使用這個框架,(個人感覺是很良心了, 內(nèi)部推動, 而不是丟出去拿用戶當(dāng)小白鼠), 然后就發(fā)現(xiàn)了很多問題, 比較突出的就是渲染問題

他們的應(yīng)用是比較復(fù)雜的, 組件樹也是非常龐大, 假設(shè)有一千個組件要渲染, 每個耗費(fèi)1ms, 一千個就是1000ms, 由于javascript 是單線程的, 這 1000ms 里 CPU 都在努力的干活, 一旦開始,中間就不會停。 如果這時候用戶去操作, 比如輸入, 點(diǎn)擊按鈕, 此時頁面是沒有響應(yīng)的。 等更新完了, 你之前的那些輸入就會啪啪啪一下子出來了。

這就是我們說的頁面卡頓, 用起來很不爽, 體驗(yàn)不好。

這個問題和設(shè)備性能沒有多大關(guān)系, 歸根結(jié)底還是同步渲染機(jī)制的問題。

目前的React 版本(v16.7), 當(dāng)組件樹很大的時候,也會出現(xiàn)這個問題, 逐層渲染, 逐漸深入,不更新完就不會停

函數(shù)調(diào)用棧如圖所示:

因?yàn)镴avaScript單線程的特點(diǎn),每個同步任務(wù)不能耗時太長,不然就會讓程序不會對其他輸入作出相應(yīng),React的更新過程就是犯了這個禁忌,而React Fiber就是要改變現(xiàn)狀。

異步渲染

Fiber 的做法是:分片。

把一個很耗時的任務(wù)分成很多小片,每一個小片的運(yùn)行時間很短,雖然總時間依然很長,但是在每個小片執(zhí)行完之后,都給其他任務(wù)一個執(zhí)行的機(jī)會,這樣唯一的線程就不會被獨(dú)占,其他任務(wù)依然有運(yùn)行的機(jī)會。 而維護(hù)每一個分片的數(shù)據(jù)結(jié)構(gòu), 就是Fiber

用一張圖來展示Fiber 的碎片化更新過程:

中間每一個波谷代表深入某個分片的執(zhí)行過程,每個波峰就是一個分片執(zhí)行結(jié)束交還控制權(quán)的時機(jī)。

更詳細(xì)的信息可以看: Lin Clark - A Cartoon Intro to Fiber - React Conf 2017

在React Fiber中,一次更新過程會分成多個分片完成,所以完全有可能一個更新任務(wù)還沒有完成,就被另一個更高優(yōu)先級的更新過程打斷,這時候,優(yōu)先級高的更新任務(wù)會優(yōu)先處理完,而低優(yōu)先級更新任務(wù)所做的工作則會完全作廢,然后等待機(jī)會重頭再來

因?yàn)橐粋€更新過程可能被打斷,所以React Fiber一個更新過程被分為兩個階段: render phase and commit phase.

兩個重要概念: render phase and commit phase

有了Fiber 之后, react 的渲染過程不再是一旦開始就不能終止的模式了, 而是劃分成為了兩個過程: 第一階段和第二階段, 也就是官網(wǎng)所謂的 render phase and commit phase

在 Render phase 中, React Fiber會找出需要更新哪些DOM,這個階段是可以被打斷的, 而到了第二階段commit phase, 就一鼓作氣把DOM更新完,絕不會被打斷。

兩個階段的分界點(diǎn)

這兩個階段, 分界點(diǎn)是什么呢?

其實(shí)是 render 函數(shù)。 而且, render 函數(shù) 也是屬于 第一階段 render phase 的

那這兩個 phase 包含的的生命周期函數(shù)有哪些呢?

render phase:

componentWillMount

componentWillReceiveProps

shouldComponentUpdate

componentWillUpdate

commit phase:

componentDidMount

componentDidUpdate

componentWillUnmount

因?yàn)榈谝浑A段的過程會被打斷而且“重頭再來”,就會造成意想不到的情況。

比如說,一個低優(yōu)先級的任務(wù)A正在執(zhí)行,已經(jīng)調(diào)用了某個組件的componentWillUpdate函數(shù),接下來發(fā)現(xiàn)自己的時間分片已經(jīng)用完了,于是冒出水面,看看有沒有緊急任務(wù),哎呀,真的有一個緊急任務(wù)B,接下來React Fiber就會去執(zhí)行這個緊急任務(wù)B,任務(wù)A雖然進(jìn)行了一半,但是沒辦法,只能完全放棄,等到任務(wù)B全搞定之后,任務(wù)A重頭來一遍,注意,是重頭來一遍,不是從剛才中段的部分開始,也就是說,componentWillUpdate函數(shù)會被再調(diào)用一次。

在現(xiàn)有的React中,每個生命周期函數(shù)在一個加載或者更新過程中絕對只會被調(diào)用一次;在React Fiber中,不再是這樣了,第一階段中的生命周期函數(shù)在一次加載和更新過程中可能會被多次調(diào)用!

這里也可以回答文行開頭的那個問題了, 當(dāng)然, 在異步渲染模式?jīng)]有開啟之前, 你可以在 willMount 里做ajax (不建議)。 首先,一個組件的 componentWillMount 比 componentDidMount 也早調(diào)用不了幾微秒,性能沒啥提高,而且如果開啟了異步渲染, 這就難受了。 React 官方也意識到了這個問題,覺得有必要去勸告(威脅, 阻止)開發(fā)者不要在render phase 里寫有副作用的代碼了(副作用:簡單說就是做本函數(shù)之外的事情,比如改一個全局變量, ajax之類)。

static getDerivedStateFromProps(nextProps, prevState) {
  //根據(jù)nextProps和prevState計算出預(yù)期的狀態(tài)改變,返回結(jié)果會被送給setState
}
新的靜態(tài)方法

為了減少(避免?)一些開發(fā)者的騷操作,React v16.3,干脆引入了一個新的生命周期函數(shù) getDerivedStateFromProps, 這個函數(shù)是一個 static 函數(shù),也是一個純函數(shù),里面不能通過 this 訪問到當(dāng)前組件(強(qiáng)制避免一些有副作用的操作),輸入只能通過參數(shù),對組件渲染的影響只能通過返回值。目的大概也是讓開發(fā)者逐步去適應(yīng)異步渲染。

我們再看一下 React v16.3 之前的的生命周期函數(shù) 示意圖:

再看看16.3的示意圖:

上圖中并包含全部React生命周期函數(shù),另外在React v16發(fā)布時,還增加了一個componentDidCatch,當(dāng)異常發(fā)生時,一個可以捕捉到異常的componentDidCatch就排上用場了。不過,很快React覺著這還不夠,在v16.6.0又推出了一個新的捕捉異常的生命周期函數(shù)getDerivedStateFromError

如果異常發(fā)生在render階段,React就會調(diào)用getDerivedStateFromError,如果異常發(fā)生在第commit階段,React會調(diào)用componentDidCatch。 這個異常可以是任何類型的異常, 捕捉到這個異常之后呢, 可以做一些補(bǔ)救之類的事情。

componentDidCatchgetDerivedStateFromError 的 區(qū)別

componentDidCatch 和 getDerivedStateFromError 都是能捕捉異常的,那他們有什么區(qū)別呢?

我們之前說了兩個階段, render phasecommit phase.

render phase 里產(chǎn)生異常的時候, 會調(diào)用 getDerivedStateFromError;

在 commit phase 里產(chǎn)生異常大的時候, 會調(diào)用 componentDidCatch

嚴(yán)格來說, 其實(shí)還有一點(diǎn)區(qū)別:

componentDidCatch 是不會在服務(wù)器端渲染的時候被調(diào)用的 而 getDerivedStateFromError 會。

背景小結(jié)

啰里八嗦一大堆, 關(guān)于背景的東西就說到這, 大家只需要了解什么是Fiber: ‘ 哦, 這個這個東西是支持異步渲染的, 雖然這個東西還沒開啟’。

然后就是渲染的兩個階段:renderphasecommit phase.

render phase 可以被打斷, 大家不要在此階段做一些有副作用的操作,可以放心在commit phase 里做。

然后就是生命周期的調(diào)整, react 把你有可能在render phase 里做的有副作用的函數(shù)都改成了static 函數(shù), 強(qiáng)迫開發(fā)者做一些純函數(shù)的操作。

現(xiàn)在我們進(jìn)入正題: SuspenseHooks

正題 suspense

Suspense要解決的兩個問題:

代碼分片;

異步獲取數(shù)據(jù)。

剛開始的時候, React 覺得自己只是管視圖的, 代碼打包的事不歸我管, 怎么拿數(shù)據(jù)也不歸我管。 代碼都打到一起, 比如十幾M, 下載就要半天,體驗(yàn)顯然不會好到哪里去。

可是后來呢,這兩個事情越來越重要, React 又覺得, 嗯,還是要摻和一下,是時候站出來展現(xiàn)真正的技術(shù)了。

Suspense 在v16.6的時候 已經(jīng)解決了代碼分片的問題,異步獲取數(shù)據(jù)還沒有正式發(fā)布。

先看一個簡單的例子:

import React from "react";
import moment from "moment";
 
const Clock = () => 

{moment().format("MMMM Do YYYY, h:mm:ss a")}

; export default Clock;

假設(shè)我們有一個組件, 是看當(dāng)前時間的, 它用了一個很大的第三方插件, 而我想只在用的時候再加載資源,不打在總包里。

再看一段代碼:

// Usage of Clock
const Clock = React.lazy(() => {
  console.log("start importing Clock");
  return import("./Clock");
});

這里我們使用了React.lazy, 這樣就能實(shí)現(xiàn)代碼的懶加載。 React.lazy 的參數(shù)是一個function, 返回的是一個promise. 這里返回的是一個import 函數(shù), webpack build 的時候, 看到這個東西, 就知道這是個分界點(diǎn)。 import 里面的東西可以打包到另外一個包里。

真正要用的話, 代碼大概是這個樣子的:

}>
  { showClock ?  : null}

showClock 為 true, 就嘗試render clock, 這時候, 就觸發(fā)另一個事件: 去加載clock.js 和它里面的 lib momment。

看到這你可能覺得奇怪, 怎么還需要用個 包起來, 有啥用, 不包行不行。

哎嗨, 不包還真是不行。 為什么呢?

前面我們說到, 目前react 的渲染模式還是同步的, 一口氣走到黑, 那我現(xiàn)在畫到clock 這里, 但是這clock 在另外一個文件里, 服務(wù)器就需要去下載, 什么時候能下載完呢, 不知道。 假設(shè)你要花十分鐘去下載, 那這十分鐘你讓react 去干啥, 總不能一直等你吧。 Suspens 就是來解決這個問題的, 你要畫clock, 現(xiàn)在沒有,那就會拋一個異常出來,我們之前說
componentDidCatch 和 getDerivedStateFromProps, 這兩個函數(shù)就是來抓子組件 或者 子子組件拋出的異常的。

子組件有異常的時候就會往上拋,直到某個組件的 getDerivedStateFromProps 抓住這個異常,抓住之后干嘛呢, 還能干嘛呀, 忍著。 下載資源的時候會拋出一個promise, 會有地方(這里是suspense)捕捉這個promise, suspense 實(shí)現(xiàn)了getDerivedStateFromProps, getDerivedStateFromProps 捕獲到異常的時候, 一看, 哎, 小老弟,你來啦,還是個promise, 然后就等這個promise resole, resolve 完成之后呢,它會嘗試重新畫一下子組件。這時候資源已經(jīng)到本地了, 也就能畫成功了。

用偽代碼 大致實(shí)現(xiàn)一下:

getDerivedStateFromError(error) {
   if (isPromise(error)) {
      error.then(reRender);
   }
}

以上大概就是Suspense 的原理, 其實(shí)也不是很復(fù)雜,就是利用了 componentDidCatch 和 getDerivedStateFromError, 其實(shí)剛開始在v16的時候, 是要用componentDidCatch 的, 但它畢竟是commit phase 里的東西, 還是分出來吧, 所以又加了個getDerivedStateFromError來實(shí)現(xiàn) Suspense 的功能。

這里需要注意的是 reRender 會渲染suspense 下面的所有子組件。

異步渲染什么時候開啟呢, 根據(jù)介紹說是在19年的第二個季度隨著一個小版本的升級開啟, 讓我們提前做好準(zhǔn)備。

做些什么準(zhǔn)備呢?

render 函數(shù)之前的代碼都檢查一邊, 避免一些有副作用的操作

到這, 我們說完了Suspense 的一半功能, 還有另一半: 異步獲取數(shù)據(jù)。

目前這一部分功能還沒正式發(fā)布。 那我們獲取數(shù)據(jù)還是只能在commit phase 做, 也就是在componentDidMount 里 或者 didUpdate 里做。

就目前來說, 如果一個組件要自己獲取數(shù)據(jù), 就必須實(shí)現(xiàn)為一個類組件, 而且會畫兩次, 第一次沒有數(shù)據(jù), 是空的, 你可以畫個loading, didMount 之后發(fā)請求, 數(shù)據(jù)回來之后, 把數(shù)據(jù)setState 到組件里, 這時候有數(shù)據(jù)了, 再畫一次,就畫出來了。

雖然是一個很簡答的功能, 我就想請求個數(shù)據(jù), 還要寫一堆東西, 很麻煩, 但在目前的正式版里, 不得不這么做。

但以后這種情況會得到改善, 看一段示例:

import {unstable_createResource as createResource} from "react-cache";

const resource = createResource(fetchDataApi);

const Foo = () => {
  const result = resource.read();
  return (
    
{result}
); // ... };

代碼里我們看不到任何譬如 async await 之類的操作, 看起來完全是同步的操作, 這是什么原理呢。

上面的例子里, 有個 resource.read(), 這里就會調(diào)api, 返回一個promise, 上面會有suspense 抓住, 等resolve 的時候,再畫一下, 就達(dá)到目的了。

到這,細(xì)心的同學(xué)可能就發(fā)現(xiàn)了一個問題, resource.read(); 明顯是一個有副作用的操作, 而且 render 函數(shù)又屬于render phase, 之前又說, 不建議在 render phase 里做有副作用的操作, 這么矛盾, 不是自己打臉了嗎。

這里也能看出來React 團(tuán)隊(duì)現(xiàn)在還沒完全想好, 目前放出來測試api 也是以unstable_開頭的, 不用用意還是跟明顯的: 讓大家不要寫class的組件,Suspense 能很好的支持函數(shù)式組件。

hooks

React v16.7.0-alpha 中第一次引入了 Hooks 的概念, 為什么要引入這個東西呢?

有兩個原因:

React 官方覺得 class組件太難以理解,OO(面向?qū)ο螅┨y懂了

React 官方覺得 , React 生命周期太難理解。

最終目的就是, 開發(fā)者不用去理解class, 也不用操心生命周期方法。

但是React 官方又說, Hooks的目的并不是消滅類組件。此處應(yīng)手動滑稽。

回歸正題, 我們繼續(xù)看Hooks, 首先看一下官方的API:

乍一看還是挺多的, 其實(shí)有很多的Hook 還處在實(shí)驗(yàn)階段,很可能有一部分要被砍掉, 目前大家只需要熟悉的, 三個就夠了:

useState

useEffect

useContext

useState

舉個例子來看下, 一個簡單的counter :

// 有狀態(tài)類組件
class Counter extends React.Component {
   state = {
      count: 0
   }
   
   increment = () => {
       this.setState({count: this.state.count + 1});
   }
   
   minus = () => {
       this.setState({count: this.state.count - 1});
   }
   
   render() {
       return (
           

{this.state.count}

); } }
// 使用useState Hook
const Counter = () => {
  const [count, setCount] = useState(0);
  
  const increment = () => setCount(count + 1);
  
  return (
    

{count}

); };

這里的Counter 不是一個類了, 而是一個函數(shù)。

進(jìn)去就調(diào)用了useState, 傳入 0,對state 進(jìn)行初始化,此時count 就是0, 返回一個數(shù)組, 第一個元素就是 state 的值,第二個元素是更新 state 的函數(shù)。

// 下面代碼等同于: const [count, setCount] = useState(0);
  const result = useState(0);
  const count = result[0];
  const setCount = result[1];

利用 count 可以讀取到這個 state,利用 setCount 可以更新這個 state,而且我們完全可以控制這兩個變量的命名,只要高興,你完全可以這么寫:

 const [theCount, updateCount] = useState(0);

因?yàn)?useState 在 Counter 這個函數(shù)體中,每次 Counter 被渲染的時候,這個 useState 調(diào)用都會被執(zhí)行,useState 自己肯定不是一個純函數(shù),因?yàn)樗獏^(qū)分第一次調(diào)用(組件被 mount 時)和后續(xù)調(diào)用(重復(fù)渲染時),只有第一次才用得上參數(shù)的初始值,而后續(xù)的調(diào)用就返回“記住”的 state 值。

讀者看到這里,心里可能會有這樣的疑問:如果組件中多次使用 useState 怎么辦?React 如何“記住”哪個狀態(tài)對應(yīng)哪個變量?

React 是完全根據(jù) useState 的調(diào)用順序來“記住”狀態(tài)歸屬的,假設(shè)組件代碼如下:

const Counter = () => {
  const [count, setCount] = useState(0);
  const [foo, updateFoo] = useState("foo");
  
  // ...
}

每一次 Counter 被渲染,都是第一次 useState 調(diào)用獲得 count 和 setCount,第二次 useState 調(diào)用獲得 foo 和 updateFoo(這里我故意讓命名不用 set 前綴,可見函數(shù)名可以隨意)。

React 是渲染過程中的“上帝”,每一次渲染 Counter 都要由 React 發(fā)起,所以它有機(jī)會準(zhǔn)備好一個內(nèi)存記錄,當(dāng)開始執(zhí)行的時候,每一次 useState 調(diào)用對應(yīng)內(nèi)存記錄上一個位置,而且是按照順序來記錄的。React 不知道你把 useState 等 Hooks API 返回的結(jié)果賦值給什么變量,但是它也不需要知道,它只需要按照 useState 調(diào)用順序記錄就好了。

你可以理解為會有一個槽去記錄狀態(tài)。

正因?yàn)檫@個原因,Hooks,千萬不要在 if 語句或者 for 循環(huán)語句中使用!

像下面的代碼,肯定會出亂子的:

const Counter = () => {
    const [count, setCount] = useState(0);
    if (count % 2 === 0) {
        const [foo, updateFoo] = useState("foo");
    }
    const [bar, updateBar] = useState("bar");
 // ...
}

因?yàn)闂l件判斷,讓每次渲染中 useState 的調(diào)用次序不一致了,于是 React 就錯亂了。

useEffect

除了 useState,React 還提供 useEffect,用于支持組件中增加副作用的支持。

在 React 組件生命周期中如果要做有副作用的操作,代碼放在哪里?

當(dāng)然是放在 componentDidMount 或者 componentDidUpdate 里,但是這意味著組件必須是一個 class。

在 Counter 組件,如果我們想要在用戶點(diǎn)擊“+”或者“-”按鈕之后把計數(shù)值體現(xiàn)在網(wǎng)頁標(biāo)題上,這就是一個修改 DOM 的副作用操作,所以必須把 Counter 寫成 class,而且添加下面的代碼:

componentDidMount() {
  document.title = `Count: ${this.state.count}`;
}

componentDidUpdate() {
  document.title = `Count: ${this.state.count}`;
}

而有了 useEffect,我們就不用寫一個 class 了,對應(yīng)代碼如下:

import { useState, useEffect } from "react";

const Counter = () => {
  const [count, setCount] = useState(0);
  
  useEffect(() => {
    document.title = `Count: ${this.state.count}`;
  });

  return (
    
{count}
); };

useEffect 的參數(shù)是一個函數(shù),組件每次渲染之后,都會調(diào)用這個函數(shù)參數(shù),這樣就達(dá)到了 componentDidMount 和 componentDidUpdate 一樣的效果。

雖然本質(zhì)上,依然是 componentDidMountcomponentDidUpdate 兩個生命周期被調(diào)用,但是現(xiàn)在我們關(guān)心的不是 mount 或者 update 過程,而是“after render”事件,useEffect 就是告訴組件在“渲染完”之后做點(diǎn)什么事。

讀者可能會問,現(xiàn)在把 componentDidMountcomponentDidUpdate 混在了一起,那假如某個場景下我只在 mount 時做事但 update 不做事,用 useEffect 不就不行了嗎?

其實(shí),用一點(diǎn)小技巧就可以解決。useEffect 還支持第二個可選參數(shù),只有同一 useEffect 的兩次調(diào)用第二個參數(shù)不同時,第一個函數(shù)參數(shù)才會被調(diào)用. 所以,如果想模擬 componentDidMount,只需要這樣寫:

  useEffect(() => {
    // 這里只有mount時才被調(diào)用,相當(dāng)于componentDidMount
  }, [123]);

在上面的代碼中,useEffect 的第二個參數(shù)是 [123],其實(shí)也可以是任何一個常數(shù),因?yàn)樗肋h(yuǎn)不變,所以 useEffect 只在 mount 時調(diào)用第一個函數(shù)參數(shù)一次,達(dá)到了 componentDidMount 一樣的效果。

useContext

在前面介紹“提供者模式”章節(jié)我們介紹過 React 新的 Context API,這個 API 不是完美的,在多個 Context 嵌套的時候尤其麻煩。

比如,一段 JSX 如果既依賴于 ThemeContext 又依賴于 LanguageContext,那么按照 React Context API 應(yīng)該這么寫:


    {
        theme => (
            
                language => {
                    //可以使用theme和lanugage了
                }
            
        )
    }

因?yàn)?Context API 要用 render props,所以用兩個 Context 就要用兩次 render props,也就用了兩個函數(shù)嵌套,這樣的縮格看起來也的確過分了一點(diǎn)點(diǎn)。

使用 Hooks 的 useContext,上面的代碼可以縮略為下面這樣:

const theme = useContext(ThemeContext);
const language = useContext(LanguageContext);
// 這里就可以用theme和language了

這個useContext把一個需要很費(fèi)勁才能理解的 Context API 使用大大簡化,不需要理解render props,直接一個函數(shù)調(diào)用就搞定。

但是,useContext也并不是完美的,它會造成意想不到的重新渲染,我們看一個完整的使用useContext的組件。

const ThemedPage = () => {
    const theme = useContext(ThemeContext);
    
    return (
       
); };

因?yàn)檫@個組件ThemedPage使用了useContext,它很自然成為了Context的一個消費(fèi)者,所以,只要Context的值發(fā)生了變化,ThemedPage就會被重新渲染,這很自然,因?yàn)椴恢匦落秩疽簿蜎]辦法重新獲得theme值,但現(xiàn)在有一個大問題,對于ThemedPage來說,實(shí)際上只依賴于theme中的color屬性,如果只是theme中的size發(fā)生了變化但是color屬性沒有變化,ThemedPage依然會被重新渲染,當(dāng)然,我們通過給Header、Content和Footer這些組件添加shouldComponentUpdate實(shí)現(xiàn)可以減少沒有必要的重新渲染,但是上一層的ThemedPage中的JSX重新渲染是躲不過去了。

說到底,useContext 需要一種表達(dá)方式告訴React:“我沒有改變,重用上次內(nèi)容好了。”

希望Hooks正式發(fā)布的時候能夠彌補(bǔ)這一缺陷。

Hooks 帶來的代碼模式改變

上面我們介紹了 useStateuseEffectuseContext 三個最基本的 Hooks,可以感受到,Hooks 將大大簡化使用 React 的代碼。

首先我們可能不再需要 class了,雖然 React 官方表示 class 類型的組件將繼續(xù)支持,但是,業(yè)界已經(jīng)普遍表示會遷移到 Hooks 寫法上,也就是放棄 class,只用函數(shù)形式來編寫組件。

對于 useContext,它并沒有為消除 class 做貢獻(xiàn),卻為消除 render props 模式做了貢獻(xiàn)。很長一段時間,高階組件和 render props 是組件之間共享邏輯的兩個武器,但如同我前面章節(jié)介紹的那樣,這兩個武器都不是十全十美的,現(xiàn)在 Hooks 的出現(xiàn),也預(yù)示著高階組件和 render props 可能要被逐步取代。

但讀者朋友,不要覺得之前學(xué)習(xí)高階組件和 render props 是浪費(fèi)時間,相反,你只有明白 React 的使用歷史,才能更好地理解 Hooks 的意義。

可以預(yù)測,在 Hooks 興起之后,共享代碼之間邏輯會用函數(shù)形式,而且這些函數(shù)會以 use- 前綴為約定,重用這些邏輯的方式,就是在函數(shù)形式組件中調(diào)用這些 useXXX 函數(shù)。

例如,我們可以寫這樣一個共享 Hook useMountLog,用于在 mount 時記錄一個日志,代碼如下:

const useMountLog = (name) => {
    useEffect(() => {
        console.log(`${name} mounted`);    
    }, [123]);
}

任何一個函數(shù)形式組件都可以直接調(diào)用這個 useMountLog 獲得這個功能,如下:

const Counter = () => {
    useMountLog("Counter");
    
    ...
}

對了,所有的 Hooks API 都只能在函數(shù)類型組件中調(diào)用,class 類型的組件不能用,從這點(diǎn)看,很顯然,class 類型組件將會走向消亡。

如何用Hooks 模擬舊版本的生命周期函數(shù)

Hooks 未來正式發(fā)布后, 我們自然而然的會遇到這個問題, 如何把寫在舊生命周期內(nèi)的邏輯遷移到Hooks里面來。下面我們就簡單說一下,

模擬整個生命周期中只運(yùn)行一次的方法
useMemo(() => {
  // execute only once
}, []);

我們可以看到useMemo 接收兩個參數(shù), 第一個參數(shù)是一個函數(shù), 第二個參數(shù)是一個數(shù)組。

這里有個地方要注意, 就是, 第二個參數(shù)的數(shù)組里的元素和上一次執(zhí)行useMemo的第二個參數(shù)的數(shù)組的元素 完全一樣的話,那就表示沒有變化, 就不用執(zhí)行第一個參數(shù)里的函數(shù)了。 如果有不同, 說明有變化, 就執(zhí)行。

上面的例子里, 我們只傳入了一個空數(shù)組, 不會有變化, 也就是只會執(zhí)行一次。

模擬shouldComponentUpdate
const areEqual = (prevProps, nextProps) => {
   // 返回結(jié)果和shouldComponentUpdate正好相反
   // 訪問不了state
}; 
React.memo(Foo, areEqual);
模擬componentDidMount
useEffect(() => {
    // 這里在mount時執(zhí)行一次
}, []);
模擬componentDidUpdate
const mounted = useRef();
useEffect(() => {
  if (!mounted.current) {
    mounted.current = true;
  } else {
    // 這里只在update是執(zhí)行
  }
});
模擬componentDidUnmount
useEffect(() => {
    // 這里在mount時執(zhí)行一次
    return () => {
       // 這里在unmount時執(zhí)行一次
    }
}, []);
未來的代碼形勢

Hooks 未來發(fā)布之后, 我們的代碼會寫成什么樣子呢? 簡單設(shè)想一下:

// Hooks之后的組件邏輯重用形態(tài)

const XXXX = () => {
  const [xx, xxx, xxxx] = useX();
  
  useY();
  
  const {a, b} = useZ();
  

  return (
    <>
     //JSX
    
  );
};

內(nèi)部可能用各種Hooks, 也可能包含第三方的Hooks。 分享Hooks 就是實(shí)現(xiàn)代碼重用的一種形勢。 其實(shí)現(xiàn)在已經(jīng)有人在做這方面的工作了: useHooks.com, 有興趣的朋友可以去看下。

Suspense 和 Hooks 帶來的改變

Suspense 和 Hooks 發(fā)布后, 會帶來什么樣的改變呢? 毫無疑問, 未來的組件, 更多的將會是函數(shù)式組件。

原因很簡單, 以后大家分享出來的都是Hooks,這東西只能在函數(shù)組件里用啊, 其他地方用不了,后面就會自然而然的發(fā)生了。

但函數(shù)式組件和函數(shù)式編程還不是同一個概念。 函數(shù)式編程必須是純的, 沒有副作用的, 函數(shù)式組件里, 不能保證, 比如那個resource.read(), 明顯是有副作用的。

關(guān)于好壞

既然這兩個東西是趨勢, 那這兩個東西到底好不好呢 ?

個人理解, 任何東西都不是十全十美。 既然大勢所趨, 我們就努力去了解它,學(xué)會它, 努力用它好的地方, 避免用不好的地方。

React 發(fā)布路線圖

最新的消息: https://reactjs.org/blog/2018...

React 16.6 with Suspense for Code Splitting (already shipped)

A minor 16.x release with React Hooks (~Q1 2019)

A minor 16.x release with Concurrent Mode (~Q2 2019)

A minor 16.x release with Suspense for Data Fetching (~mid 2019)

明顯能夠看到資源在往 Suspense 和 Hooks 傾斜。

結(jié)語

看到這, 相信大家都Suspense 和 Hooks 都有了一個大概的了解了。

收集各種資料花費(fèi)了挺長時間,大概用了兩三天寫出來,中間參考了很多資料, 一部分是摘錄到了上面的內(nèi)容里。

在這里整理分享一下, 希望對大家有所幫助。

才疏學(xué)淺, 難免會有紕漏, 歡迎指正:)。

參考資料

https://reactjs.org/docs/hook...

https://zhuanlan.zhihu.com/ad...

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/100322.html

相關(guān)文章

  • React V16.9來了 無痛感升級 加入性能檢測 【譯-真香】

    摘要:更新日志應(yīng)對添加以編程方式收集性能測量。在和在將棄用為常見攻擊面。添加對事件的支持。在從調(diào)用時發(fā)出警告,創(chuàng)建循環(huán)。在和從錯誤的渲染器使用時發(fā)出警告。 2019年8月8日,我們發(fā)布了React 16.9。它包含幾個新功能,錯誤修正和新的棄用警告,以幫助準(zhǔn)備未來的主要版本。 showImg(https://segmentfault.com/img/bVbwoB5?w=1728&h=666)...

    zzir 評論0 收藏0
  • React V16.9來了 無痛感升級 加入性能檢測 【譯-真香】

    摘要:更新日志應(yīng)對添加以編程方式收集性能測量。在和在將棄用為常見攻擊面。添加對事件的支持。在從調(diào)用時發(fā)出警告,創(chuàng)建循環(huán)。在和從錯誤的渲染器使用時發(fā)出警告。 2019年8月8日,我們發(fā)布了React 16.9。它包含幾個新功能,錯誤修正和新的棄用警告,以幫助準(zhǔn)備未來的主要版本。 showImg(https://segmentfault.com/img/bVbwoB5?w=1728&h=666)...

    ky0ncheng 評論0 收藏0
  • React 源碼漂流(一)之 起航

    摘要:在前端開發(fā)過程中,源碼解讀是必不可少的一個環(huán)節(jié),我們直接進(jìn)入主題,注意當(dāng)前版本號。注意包文件僅僅是的必要的功能性的定義,它必須要結(jié)合一起使用下是,原生環(huán)境下是。 在前端開發(fā)過程中,源碼解讀是必不可少的一個環(huán)節(jié),我們直接進(jìn)入主題,注意當(dāng)前 React 版本號 16.8.6。 注意:react 包文件僅僅是 React components 的必要的、功能性的定義,它必須要結(jié)合 React...

    Mr_zhang 評論0 收藏0
  • React Hooks 越來越火了,它會取代傳統(tǒng)的 Redux 嗎?

    摘要:前言自推出之后,收到了不少追捧,很多問題也隨之而來。在出現(xiàn)之前,可以使用保存狀態(tài)和更新狀態(tài)用以應(yīng)對這種情況。為了在這個用例上追趕的腳步,的需要提供副作用隔離功能。提供了一個,可以用它接入你的風(fēng)格的。 showImg(https://segmentfault.com/img/remote/1460000019913697?w=1280&h=853); 前言 React Hooks 自推出...

    Scliang 評論0 收藏0
  • 精讀《React16 新特性》

    摘要:引言于發(fā)布版本,時至今日已更新到,且引入了大量的令人振奮的新特性,本文章將帶領(lǐng)大家根據(jù)更新的時間脈絡(luò)了解的新特性。其作用是根據(jù)傳遞的來更新。新增等指針事件。 1 引言 于 2017.09.26 Facebook 發(fā)布 React v16.0 版本,時至今日已更新到 React v16.6,且引入了大量的令人振奮的新特性,本文章將帶領(lǐng)大家根據(jù) React 更新的時間脈絡(luò)了解 React1...

    Nosee 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<