React 16 以前的版本比對更新 VirtualDOM 的過程是採用循環加遞歸實現的,這種比對方式有一個問題,就是一旦任務開始進行就沒法中斷,若是應用中組件數量龐大,主線程被長期佔用,直到整棵 VirtualDOM 樹比對更新完成以後主線程才能被釋放,主線程才能執行其餘任務。這就會致使一些用戶交互,動畫等任務沒法當即獲得執行,頁面就會產生卡頓, 很是的影響用戶體驗。html
Fiber就是React提出的用於解決頁面卡頓的方案,包含以下三個方面:算法
requestIdleCallback 是瀏覽器提供的API,其利用瀏覽器的空閒時間執行任務,若是有更高優先級的任務須要執行時,當前執行的任務可會被終止,優先執行更高優先級的任務。數組
requestIdleCallback接受一個函數做爲參數,該函數是要執行的任務:瀏覽器
requestIdleCallback(function(deadline) { // deadline.timeRemaining() 獲取瀏覽器的空餘時間 })
在下面的html實例中,頁面上包含兩個按鈕,點擊第一個按鈕執行一段耗時操做,點擊另外一個按鈕alert顯示一段內容:dom
<!DOCTYPE html> <html> <head> <meta charset='utf-8'> <meta http-equiv='X-UA-Compatible' content='IE=edge'> <title>Page Title</title> <meta name='viewport' content='width=device-width, initial-scale=1'> </head> <body> <button id="work">long time work</button> <button id="interaction">another work</button> </body> <script> var workBtn = document.getElementById("work") var interactionBtn = document.getElementById("interaction") var iterationCount = 100000000 var value = 0 // 模擬一段耗時操做 workBtn.addEventListener("click", function () { while (iterationCount > 0) { value = Math.random() < 0.5 ? value + Math.random() : value + Math.random() iterationCount = iterationCount - 1 } }) interactionBtn.addEventListener("click", function () { alert('done another work') }) </script> </html>
當點擊第一個按鈕以後迅速點擊第二個按鈕,會發現頁面會卡頓一段時間以後才執行alert。函數
當用requestIdleCallback API改造以後:動畫
<!DOCTYPE html> <html> <head> <meta charset='utf-8'> <meta http-equiv='X-UA-Compatible' content='IE=edge'> <title>Page Title</title> <meta name='viewport' content='width=device-width, initial-scale=1'> </head> <body> <button id="work">long time work</button> <button id="interaction">another work</button> </body> <script> var workBtn = document.getElementById("work") var interactionBtn = document.getElementById("interaction") var iterationCount = 100000000 var value = 0 // 模擬一段耗時操做 var expensiveCalculation = function (IdleDeadline) { // 空閒時間超過1秒才執行 while (iterationCount > 0 && IdleDeadline.timeRemaining() > 1) { value = Math.random() < 0.5 ? value + Math.random() : value + Math.random() iterationCount = iterationCount - 1 } requestIdleCallback(expensiveCalculation) } workBtn.addEventListener("click", function () { requestIdleCallback(expensiveCalculation) }) interactionBtn.addEventListener("click", function () { alert('done another work') }) </script> </html>
再次快速點擊第一個按鈕和第二個按鈕,會發現,頁面迅速alert一段信息,說明第一個任務並無阻塞第二個任務。ui
Fiber將Dom對比算法分解成兩步:線程
爲了可以模擬實現整個Fiber的核心代碼,須要首先了解Fiber對象的結構,Fiber對象是一個普通的js對象,其包含以下屬性:code
屬性名 | 說明 |
---|---|
type | 節點類型,和虛擬Dom對象的type相同,用於區分元素、文本、組件 |
props | 節點屬性,同虛擬Dom對象 |
stateNode | 節點Dom對象或者組件實例 |
tag | 標記,用於標記節點 |
effects | 存儲包含自身和全部後代的Fiber數組 |
effectTag | 標記當前節點須要進行的操做,包含插入、更新、移除等 |
parent | 父Fiber對象,在React源碼中叫Return |
child | 當前Fiber對象的子級Fiber對象 |
sibling | 當前Fiber對象的下一級兄弟節點 |
alternate | Fiber對象備份,用於對比 |
最終虛擬Dom樹會被轉換成Fiber對象的樹形結構數據,最頂層的節點effects屬性中包含了該樹結構全部的Fiber對象,其是一個數組,也就是前文說的能被中斷的一個個小任務的任務操做對象。