日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷解決方案
聊聊React內(nèi)部的性能優(yōu)化沒有達(dá)到極致?

大家好,我卡頌。

成都創(chuàng)新互聯(lián)主要從事網(wǎng)站建設(shè)、成都網(wǎng)站建設(shè)、網(wǎng)頁設(shè)計(jì)、企業(yè)做網(wǎng)站、公司建網(wǎng)站等業(yè)務(wù)。立足成都服務(wù)新邱,十載網(wǎng)站建設(shè)經(jīng)驗(yàn),價(jià)格優(yōu)惠、服務(wù)專業(yè),歡迎來電咨詢建站服務(wù):18982081108

對(duì)于如下這個(gè)常見交互步驟:

  1. 點(diǎn)擊按鈕,觸發(fā)狀態(tài)更新。
  2. 組件render。
  3. 視圖渲染。

你覺得哪些步驟有「性能優(yōu)化的空間」呢?

答案是:1和2。

對(duì)于「步驟1」,如果狀態(tài)更新前后沒有變化,則可以略過剩下的步驟。這個(gè)優(yōu)化策略被稱為eagerState。

對(duì)于「步驟2」,如果組件的子孫節(jié)點(diǎn)沒有狀態(tài)變化,可以跳過子孫組件的render。這個(gè)優(yōu)化策略被稱為bailout。

看起來eagerState的邏輯很簡(jiǎn)單,只需要比較「狀態(tài)更新前后是否有變化」。

然而,實(shí)踐上卻很復(fù)雜。

本文通過了解eagerState的邏輯,回答一個(gè)問題:React的性能優(yōu)化達(dá)到極致了么?

一個(gè)奇怪的例子

考慮如下組件:

function App() {
const [num, updateNum] = useState(0);
console.log("App render", num);
return (
updateNum(1)}>


);
}
function Child() {
console.log("child render");
return child;
}

在線Demo地址[1]。

首次渲染,打?。?/p>

App render 0
child render

第一次點(diǎn)擊div,打?。?/p>

App render 1
child render

第二次點(diǎn)擊div,打?。?/p>

App render 1

第三、四......次點(diǎn)擊div,不打印。

在「第二次」點(diǎn)擊中,打印了App render 1,沒有打印child render。代表App的子孫組件沒有render,命中了bailout。

「第三次及之后」的點(diǎn)擊,什么都不打印,代表沒有組件render,命中了eagerState。

那么問題來了,明明第一、二次點(diǎn)擊都是執(zhí)行updateNum(1),顯然狀態(tài)是沒有變化的,為什么第二次沒有命中eagerState?

eagerState的觸發(fā)條件

首先我們需要明白,為什么叫eagerState(急迫的狀態(tài))?

通常,什么時(shí)候能獲取到最新狀態(tài)呢?組件render的時(shí)候。

當(dāng)組件render,useState執(zhí)行并返回最新狀態(tài)。

考慮如下代碼:

const [num, updateNum] = useState(0);

useState執(zhí)行后返回的num就是最新狀態(tài)。

之所以u(píng)seState執(zhí)行時(shí)才能計(jì)算出最新狀態(tài),是因?yàn)闋顟B(tài)是根據(jù)「一到多個(gè)更新」計(jì)算而來的。

比如,在如下點(diǎn)擊事件中觸發(fā)3個(gè)更新:

const onClick = () => {
updateNum(100);
updateNum(num => num + 1);
updateNum(num => num * 2);
}

組件render時(shí)num的最新狀態(tài)應(yīng)該是多少呢?

  • 首先num變?yōu)?00。
  • 100 + 1 = 101。
  • 101 * 2 = 202。

所以,useState會(huì)返回202作為num的最新狀態(tài)。

實(shí)際情況會(huì)更復(fù)雜,更新?lián)碛凶约旱膬?yōu)先級(jí),所以在render前不能確定「究竟是哪些更新會(huì)參與狀態(tài)的計(jì)算」。

所以,在這種情況下組件必須render,useState必須執(zhí)行才能知道num的最新狀態(tài)是多少。

那就沒法提前將num的最新狀態(tài)與num的當(dāng)前狀態(tài)比較,判斷「狀態(tài)是否變化」。

而eagerState的意義在于,在「某種情況」下,我們可以在組件render前就提前計(jì)算出最新狀態(tài)(這就是eagerState的由來)。

這種情況下組件不需要render就能比較「狀態(tài)是否變化」。

那么是什么情況呢?

答案是:當(dāng)前組件上「不存在更新」的時(shí)候。

當(dāng)不存在更新時(shí),本次更新就是組件的第一個(gè)更新。在只有一個(gè)更新的情況下是能確定最新狀態(tài)的。

所以,eagerState的前提是:

當(dāng)前組件不存在更新,那么首次觸發(fā)狀態(tài)更新時(shí),就能立刻計(jì)算出最新狀態(tài),進(jìn)而與當(dāng)前狀態(tài)比較。

如果兩者一致,則省去了后續(xù)render的過程。

這就是eagerState的邏輯。但遺憾的是,實(shí)際情況還要再?gòu)?fù)雜一丟丟。

先讓我們看一個(gè)「看似不相干」的例子。

必要的React源碼知識(shí)

對(duì)于如下組件:

function App() {
const [num, updateNum] = useState(0);
window.updateNum = updateNum;
return
{num}
;
}

在控制臺(tái)執(zhí)行如下代碼,可以改變視圖顯示的num么?

window.updateNum(100)

答案是:可以。

因?yàn)锳pp組件對(duì)應(yīng)fiber(保存組件相關(guān)信息的節(jié)點(diǎn))已經(jīng)被作為「預(yù)設(shè)的參數(shù)」傳遞給window.updateNum了:

// updateNum的實(shí)現(xiàn)類似這樣
// 其中fiber就是App對(duì)應(yīng)fiber
const updateNum = dispatchSetState.bind(null, fiber, queue);

所以u(píng)pdateNum執(zhí)行時(shí)是能獲取App對(duì)應(yīng)fiber的。

然而,一個(gè)組件實(shí)際有2個(gè)fiber,他們:

  • 一個(gè)保存「當(dāng)前視圖」對(duì)應(yīng)的相關(guān)信息,被稱為current fiber。
  • 一個(gè)保存「接下來要變化的視圖」對(duì)應(yīng)的相關(guān)信息,被稱為wip fiber。

updateNum中被預(yù)設(shè)的是wip fiber。

當(dāng)組件觸發(fā)更新后,會(huì)在組件對(duì)應(yīng)的2個(gè)fiber上都「標(biāo)記更新」。

當(dāng)組件render時(shí),useState會(huì)執(zhí)行,計(jì)算出新的狀態(tài),并把wip fiber上的「更新標(biāo)記」清除。

當(dāng)視圖完成渲染后,current fiber與wip fiber會(huì)交換位置(也就是說本次更新的wip fiber會(huì)變?yōu)橄麓胃碌腸urrent fiber)。

回到例子

剛才談到,eagerState的前提是:「當(dāng)前組件不存在更新」。

具體來講,是組件對(duì)應(yīng)的current fiber與wip fiber都不存在更新。

回到我們的例子:

第一次點(diǎn)擊div,打?。?/p>

App render 1
child render

current fiber與wip fiber同時(shí)標(biāo)記更新。

render后wip fiber的「更新標(biāo)記」清除。

此時(shí)current fiber還存在「更新標(biāo)記」。

完成渲染后,current fiber與wip fiber會(huì)交換位置。

變成:wip fiber存在更新,current fiber不存在更新。

所以第二次點(diǎn)擊div時(shí),由于wip fiber存在更新,沒有命中eagerState,于是打?。?/p>

App render 1

render后wip fiber的「更新標(biāo)記」清除。

此時(shí)兩個(gè)fiber上都不存在「更新標(biāo)記」。所以后續(xù)點(diǎn)擊div都會(huì)觸發(fā)eagerState,組件不會(huì)render。

總結(jié)

由于React內(nèi)部各個(gè)部分間互相影響,導(dǎo)致React性能優(yōu)化的結(jié)果有時(shí)讓開發(fā)者迷惑。

為什么沒有聽到多少人抱怨呢?因?yàn)樾阅軆?yōu)化只會(huì)反映在指標(biāo)上,不會(huì)影響交互邏輯。

通過本文我們發(fā)現(xiàn),React性能優(yōu)化并沒有做到極致,由于存在兩個(gè)fiber,eagerState策略并沒有達(dá)到最理想的狀態(tài)。

參考資料

[1]在線Demo地址:


網(wǎng)站標(biāo)題:聊聊React內(nèi)部的性能優(yōu)化沒有達(dá)到極致?
鏈接URL:http://m.5511xx.com/article/cocoeih.html