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