摘要:今天將手寫一個(gè),詳細(xì)講解遍歷鏈的實(shí)現(xiàn)方式。可以看到循環(huán)的結(jié)束條件是當(dāng)前處理的節(jié)點(diǎn)等于根節(jié)點(diǎn)。下面再來看看怎么結(jié)合,實(shí)現(xiàn)漸進(jìn)式遍歷。
歡迎關(guān)注我的公眾號(hào)睿Talk,獲取我最新的文章:
之前寫的一篇文章,React Fiber 原理介紹,介紹了 React Fiber 的實(shí)現(xiàn)原理,其中的關(guān)鍵是使用Fiber鏈的數(shù)據(jù)結(jié)構(gòu),將遞歸的Stack Reconciler改寫為循環(huán)的Fiber Reconciler。今天將手寫一個(gè) demo,詳細(xì)講解遍歷Fiber鏈的實(shí)現(xiàn)方式。
二、Stack Reconciler假設(shè)有以下組件樹:
對(duì)應(yīng)的 JS 代碼如下:
const a1 = {name: "a1"}; const b1 = {name: "b1"}; const b2 = {name: "b2"}; const b3 = {name: "b3"}; const c1 = {name: "c1"}; const c2 = {name: "c2"}; const d1 = {name: "d1"}; const d2 = {name: "d2"}; a1.render = () => [b1, b2, b3]; b1.render = () => []; b2.render = () => [c1]; b3.render = () => [c2]; c1.render = () => [d1, d2]; c2.render = () => []; d1.render = () => []; d2.render = () => [];
使用Stack Reconciler遞歸的方式來遍歷組件樹,大概是這個(gè)樣子:
function doWork(o) { console.log(o.name); } function walk(instance) { doWork(instance); const children = instance.render(); children.forEach(walk); } walk(a1); // 輸出結(jié)果:a1, b1, b2, c1, d1, d2, b3, c2二、Fiber Reconciler
下面我們用 Fiber 的數(shù)據(jù)結(jié)構(gòu)來改寫遍歷過程。首先定義數(shù)據(jù)結(jié)構(gòu),然后在遍歷的過程中通過link方法創(chuàng)建節(jié)點(diǎn)間的關(guān)系:
// 定義 Fiber 數(shù)據(jù)結(jié)構(gòu) class Node { constructor(instance) { this.instance = instance; this.child = null; this.sibling = null; this.return = null; } } // 創(chuàng)建關(guān)系鏈 function link(parent, children) { if (children === null) children = []; // child 指向第一個(gè)子元素 parent.child = children.reduceRight((previous, current) => { const node = new Node(current); node.return = parent; // sibling 指向前面處理的元素 node.sibling = previous; return node; }, null); return parent.child; }
遍歷完成后會(huì)得出如下的關(guān)系鏈:
下面來詳細(xì)看下遍歷的過程。還是沿用之前的walk和doWork方法名:
function doWork(node) { console.log(node.instance.name); // 創(chuàng)建關(guān)系鏈 const children = node.instance.render(); return link(node, children); } function walk() { while (true) { let child = doWork(node); if (child) { node = child; continue; } if (node === root) { return; } while (!node.sibling) { if (!node.return || node.return === root) { return; } node = node.return; } node = node.sibling; } } const hostNode = new Node(a1); const root = hostNode; let node = root; walk(); // 輸出結(jié)果:a1, b1, b2, c1, d1, d2, b3, c2
上面就是遞歸改循環(huán)的代碼了。可以看到循環(huán)的結(jié)束條件是當(dāng)前處理的節(jié)點(diǎn)等于根節(jié)點(diǎn)。在循環(huán)開始的時(shí)候,以深度優(yōu)先一層一層往下遞進(jìn)。當(dāng)沒有子節(jié)點(diǎn)和兄弟節(jié)點(diǎn)的時(shí)候,當(dāng)前節(jié)點(diǎn)會(huì)往上層節(jié)點(diǎn)回溯,直至根節(jié)點(diǎn)為止。
下面再來看看怎么結(jié)合requestIdleCallback API,實(shí)現(xiàn)漸進(jìn)式遍歷。由于完成這個(gè)遍歷所需時(shí)間實(shí)在太短,因此每處理 3 個(gè)節(jié)點(diǎn),我們sleep 1 秒,從而達(dá)到退出當(dāng)前requestIdleCallback的目的,然后再創(chuàng)建一個(gè)新的回調(diào)任務(wù):
function sleep(n) { const start = +new Date(); while(true) if(+new Date() - start > n) break; } function walk(deadline) { let i = 1; while (deadline.timeRemaining() > 0 || deadline.didTimeout) { console.log(deadline.timeRemaining(), deadline.didTimeout); let child = doWork(node); if (i > 2) { sleep(1000); } i++; if (child) { node = child; continue; } if (node === root) { console.log("================ Task End ==============="); return; } while (!node.sibling) { if (!node.return || node.return === root) { console.log("================ Task End ==============="); return; } node = node.return; } node = node.sibling; } console.log("================ Task End ==============="); requestIdleCallback(walk); } requestIdleCallback(walk); // 輸出結(jié)果: 15.845 false a1 15.14 false b1 14.770000000000001 false b2 ================ Task End =============== 15.290000000000001 false c1 14.825000000000001 false d1 14.485000000000001 false d2 ================ Task End =============== 14.96 false b3 14.475000000000001 false c2 ================ Task End ===============三、總結(jié)
本文通過一個(gè) demo,講解了如何利用React Fiber的數(shù)據(jù)結(jié)構(gòu),遞歸改循環(huán),實(shí)現(xiàn)組件樹的漸進(jìn)式遍歷。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/109642.html
摘要:基礎(chǔ)的理論概念這篇文章是我的一次嘗試,希望能夠形式化的介紹關(guān)于本身的一些理念模型。我對(duì)于此實(shí)際的理念模型是在每次的更新過程中返回下一個(gè)階段的狀態(tài)。的目標(biāo)是提升對(duì)在動(dòng)畫,布局以及手勢(shì)方面的友好度。我已經(jīng)邀請(qǐng)了團(tuán)隊(duì)的成員來對(duì)本文檔的準(zhǔn)確性進(jìn)行。 前言 本文主要是對(duì)收集到的一些官方或者其他平臺(tái)的文章進(jìn)行翻譯,中間可能穿插一些個(gè)人的理解,如有錯(cuò)誤疏漏之處,還望批評(píng)指正。筆者并未研究過源碼,只是...
摘要:如果運(yùn)算持續(xù)占用主線程,頁面就沒法得到及時(shí)的更新。三解題思路解決主線程長(zhǎng)時(shí)間被運(yùn)算占用這一問題的基本思路,是將運(yùn)算切割為多個(gè)步驟,分批完成。這顆新樹每生成一個(gè)新的節(jié)點(diǎn),都會(huì)將控制權(quán)交回給主線程,去檢查有沒有優(yōu)先級(jí)更高的任務(wù)需要執(zhí)行。 歡迎關(guān)注我的公眾號(hào)睿Talk,獲取我最新的文章:showImg(https://segmentfault.com/img/bVbmYjo); 一、前言 在...
摘要:接下來我們就是正式的工作了,用循環(huán)從某個(gè)節(jié)點(diǎn)開始遍歷樹。最后一步判斷全局變量是否存在,如果存在則把這次遍歷樹產(chǎn)生的所有更新一次更新到真實(shí)的上去。 前情提要 上一篇我們提到如果 setState 之后,虛擬 dom diff 比較耗時(shí),那么導(dǎo)致瀏覽器 FPS 降低,使得用戶覺得頁面卡頓。那么 react 新的調(diào)度算法就是把原本一次 diff 的過程切分到各個(gè)幀去執(zhí)行,使得瀏覽器在 dif...
摘要:在上面我們已經(jīng)知道瀏覽器是一幀一幀執(zhí)行的,在兩個(gè)執(zhí)行幀之間,主線程通常會(huì)有一小段空閑時(shí)間,可以在這個(gè)空閑期調(diào)用空閑期回調(diào),執(zhí)行一些任務(wù)。另外由于這些堆棧是可以自己控制的,所以可以加入并發(fā)或者錯(cuò)誤邊界等功能。 文章首發(fā)于個(gè)人博客 前言 2016 年都已經(jīng)透露出來的概念,這都 9102 年了,我才開始寫 Fiber 的文章,表示慚愧呀。不過現(xiàn)在好的是關(guān)于 Fiber 的資料已經(jīng)很豐富了,...
摘要:開始寫代碼構(gòu)造函數(shù)講了那么多的理論,大家一定是暈了,但是沒辦法,架構(gòu)已經(jīng)比之前的簡(jiǎn)單要復(fù)雜太多了,因此不可能指望一次性把的內(nèi)容全部理解,需要反復(fù)多看。 前言 Facebook 的研發(fā)能力真是驚人, Fiber 架構(gòu)給 React 帶來了新視野的同時(shí),將調(diào)度一詞介紹給了前端,然而這個(gè)架構(gòu)實(shí)在不好懂,比起以前的 Vdom 樹,新的 Fiber 樹就麻煩太多。 可以說,React 16 和 ...
閱讀 2833·2023-04-25 18:58
閱讀 977·2021-11-25 09:43
閱讀 1210·2021-10-25 09:46
閱讀 3494·2021-09-09 11:40
閱讀 1679·2021-08-05 09:59
閱讀 869·2019-08-29 15:07
閱讀 956·2019-08-29 12:48
閱讀 695·2019-08-29 11:19