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