摘要:可能存在的問題在中,更新是分階段的,具體分為兩個階段,首先是的階段,這個階段在計算前后樹的差異,然后是的階段,這個階段將把更新渲染到頁面上。官方目前已經(jīng)把和標(biāo)記為,并使用新的生命周期函數(shù)和進(jìn)行替換。
React Fiber
Diff 算法
熟悉 react 的朋友都知道,在 react 中有個核心的算法,叫 diff 算法。web 界面由 dom 樹組成,不同的 dom 樹會渲染出不同的界面。react 使用 virtual dom 來表示 dom 樹,而 diff 算法就是用于比較 virtual dom 樹的區(qū)別,并更新界面需要更新的部分。diff 算法和 virtual dom 的完美結(jié)合的過程被稱為 reconciler,這可是 react 攻城拔寨的絕對利器。有了 reconciler,開發(fā)者可以脫身操作真實的 dom 樹,只需要向 react 描述界面的狀態(tài),而 react 會幫助你高效的完成真正 dom 操作。
在 react16 之前的 reconciler 叫 stack reconciler,fiber 是 react 新的 reconciler,這次更新到 fiber 架構(gòu)是一次重量級的核心架構(gòu)的替換,react 為了完成這次替換已經(jīng)準(zhǔn)備了兩三年的時間了。
那么 fiber 究竟有什么好的呢?
Fiber 為何出現(xiàn)不知道大家有沒有遇到過這樣的情況,點擊一個頁面的按鈕時感覺到頁面沒有任何的反應(yīng),讓你懷疑電腦是不是死機(jī)了,然后你快速切出瀏覽器,發(fā)現(xiàn)電腦并沒有死機(jī),于是再切回瀏覽器,這時候才發(fā)現(xiàn)頁面終于更新了。為什么會出現(xiàn)這種情況?在多數(shù)情況下,可能是因為瀏覽器忙著執(zhí)行相關(guān)的 js 代碼,導(dǎo)致瀏覽器主線程沒有及時響應(yīng)用戶的操作或者沒有及時更新界面。下面這張圖就表示了這種現(xiàn)象,你的公司只有一個程序員 (main thread),當(dāng)這個程序員在執(zhí)行你的任務(wù) (your code) 時,處于沉浸式編程的狀態(tài),無法響應(yīng)外部的其他事件,什么下班吃飯,都是不存在的。這就像瀏覽器忙著執(zhí)行 js 代碼的時候,不會去執(zhí)行頁面更新等操作。
本著顧客是上帝的原則,作為一名優(yōu)秀的開發(fā)者,怎么能夠允許出現(xiàn)這種情況降低用戶的體驗?zāi)?。因?react 團(tuán)隊引入了異步渲染這個概念,而采用 fiber 架構(gòu)可以實現(xiàn)這種異步渲染的方式。
原先的 stack reconciler 像是一個遞歸執(zhí)行的函數(shù),從父組件調(diào)用子組件的 reconciler 過程就是一個遞歸執(zhí)行的過程,這也是為什么被稱為 stack reconciler 的原因。當(dāng)我們調(diào)用 setState 的時候,react 從根節(jié)點開始遍歷,找出所有的不同,而對于特別龐大的 dom 樹來說,這個遞歸遍歷的過程會消耗特別長的時間。在這個期間,任何交互和渲染都會被阻塞,這樣就給用戶一種“死機(jī)”的感覺。
fiber 的出現(xiàn)解決了這個問題,它把 reconciler 的過程拆分成了一個個的小任務(wù),并在完成了小任務(wù)之后暫停執(zhí)行 js 代碼,然后檢查是否有需要更新的內(nèi)容和需要響應(yīng)的事件,做出相應(yīng)的處理后再繼續(xù)執(zhí)行 js 代碼。這樣就給了用戶一種應(yīng)用一直在運行的感覺,提高了用戶的體驗。
在做顯示方面的工作時,經(jīng)常會聽到一個目標(biāo)叫 60 幀,這表示的是畫面的更新頻率,也就是畫面每秒鐘更新 60 次。這是因為在 60 幀的更新頻率下,頁面在人眼中顯得流暢,無明顯卡頓。每秒鐘更新 60 次也就是每 16ms 需要更新一次頁面,如果更新頁面消耗的時間不到 16ms,那么在下一次更新時機(jī)來到之前會剩下一點時間執(zhí)行其他的任務(wù),只要保證及時在 16ms 的間隔下更新界面就完全不會影響到頁面的流暢程度。fiber 的核心正是利用了 60 幀原則,實現(xiàn)了一個基于優(yōu)先級和 requestIdleCallback 的循環(huán)任務(wù)調(diào)度算法。
requestIdleCallback 是瀏覽器提供的一個 api,可以讓瀏覽器在空閑的時候執(zhí)行回調(diào),在回調(diào)參數(shù)中可以獲取到當(dāng)前幀剩余的時間,fiber 利用了這個參數(shù),判斷當(dāng)前剩下的時間是否足夠繼續(xù)執(zhí)行任務(wù),如果足夠則繼續(xù)執(zhí)行,否則暫停任務(wù),并調(diào)用 requestIdleCallback 通知瀏覽器空閑的時候繼續(xù)執(zhí)行當(dāng)前的任務(wù)。
function fiber(剩余時間) { if (剩余時間 > 任務(wù)所需時間) { 做任務(wù); } else { requestIdleCallback(fiber); } }
fiber 還會為不同的任務(wù)設(shè)置不同的優(yōu)先級,高優(yōu)先級任務(wù)是需要馬上展示到頁面上的,比如你正在輸入框中輸入文字,你肯定希望你的手指在鍵盤上敲下每一個按鍵時,輸入框能立馬做出反饋,這樣你才能知道你的輸入是否正確,是否有效。低優(yōu)先級的任務(wù)則是像從服務(wù)器傳來了一些數(shù)據(jù),這個時候需要更新頁面,比如這篇文章喜歡的人數(shù)+1 或是評論+1,這并不是那么緊急的更新,延遲 100-200ms 并不會有多大差別,完全可以在后面進(jìn)行處理。fiber 會根據(jù)任務(wù)優(yōu)先級來動態(tài)調(diào)整任務(wù)調(diào)度,優(yōu)先完成高優(yōu)先級的任務(wù)。
{ Synchronous: 1, // 同步任務(wù),優(yōu)先級最高 Task: 2, // 當(dāng)前調(diào)度正執(zhí)行的任務(wù) Animation 3, // 動畫 High: 4, // 高優(yōu)先級 Low: 5, // 低優(yōu)先級 Offscreen: 6, // 當(dāng)前屏幕外的更新,優(yōu)先級最低 }
在 fiber 架構(gòu)中,有一種數(shù)據(jù)結(jié)構(gòu),它的名字就叫做 fiber,這也是為什么新的 reconciler 叫做 fiber 的原因。fiber 其實就是一個 js 對象,這個對象的屬性中比較重要的有 stateNode、tag、return、child、sibling 和 alternate。
Fiber = { tag // 標(biāo)記任務(wù)的進(jìn)度 return // 父節(jié)點 child // 子節(jié)點 sibling // 兄弟節(jié)點 alternate // 變化記錄 ..... };
我們可以看出 fiber 基于鏈表結(jié)構(gòu),擁有一個個指針,指向它的父節(jié)點子節(jié)點和兄弟節(jié)點,在 diff 的過程中,依照節(jié)點連接的關(guān)系進(jìn)行遍歷。
fiber 可能存在的問題
在 fiber 中,更新是分階段的,具體分為兩個階段,首先是 reconciliation 的階段,這個階段在計算前后 dom 樹的差異,然后是 commit 的階段,這個階段將把更新渲染到頁面上。第一個階段是可以打斷的,因為這個階段耗時可能會很長,因此需要暫停下來去執(zhí)行其他更高優(yōu)先級的任務(wù),第二個階段則不會被打斷,會一口氣把更新渲染到頁面上。
由于 reconciliation 的階段會被打斷,可能會導(dǎo)致 commit 前的這些生命周期函數(shù)多次執(zhí)行。react 官方目前已經(jīng)把 componentWillMount、componentWillReceiveProps 和 componetWillUpdate 標(biāo)記為 unsafe,并使用新的生命周期函數(shù) getDerivedStateFromProps 和 getSnapshotBeforeUpdate 進(jìn)行替換。
還有一個問題是饑餓問題,意思是如果高優(yōu)先級的任務(wù)一直插入,導(dǎo)致低優(yōu)先級的任務(wù)無法得到機(jī)會執(zhí)行,這被稱為饑餓問題。對于這個問題官方提出的解決方案是盡量復(fù)用已經(jīng)完成的操作來緩解。相信官方也正在努力提出更好的方法去解決這個問題。
原文鏈接:https://knownsec-fed.com/2018...
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/104164.html
摘要:我們可以為元素添加屬性然后在回調(diào)函數(shù)中接受該元素在樹中的句柄,該值會作為回調(diào)函數(shù)的第一個參數(shù)返回。使用最常見的用法就是傳入一個對象。單向數(shù)據(jù)流,比較有序,有便于管理,它隨著視圖庫的開發(fā)而被概念化。 面試中問框架,經(jīng)常會問到一些原理性的東西,明明一直在用,也知道怎么用, 但面試時卻答不上來,也是挺尷尬的,就干脆把react相關(guān)的問題查了下資料,再按自己的理解整理了下這些答案。 reac...
摘要:正文在回復(fù)中表示為什么是異步的,這并沒有一個明顯的答案,每種方案都有它的權(quán)衡。需要注意的是,異步更新是有可能實現(xiàn)這種設(shè)想的前提。 前言 不知道大家有沒有過這個疑問,React 中 setState() 為什么是異步的?我一度認(rèn)為 setState() 是同步的,知道它是異步的之后很是困惑,甚至期待 React 能出一個 setStateSync() 之類的 API。同樣有此疑問的還有 ...
摘要:他們的應(yīng)用是比較復(fù)雜的,組件樹也是非常龐大,假設(shè)有一千個組件要渲染,每個耗費一千個就是由于是單線程的,這里都在努力的干活,一旦開始,中間就不會停。 悄悄的, React v16.7 發(fā)布了。 React v16.7: No, This Is Not The One With Hooks. showImg(https://segmentfault.com/img/bVblq9L?w=97...
摘要:的參數(shù)既可以是一個對象,也可以是一個回調(diào)函數(shù)?;卣{(diào)函數(shù)提供了兩個參數(shù),第一個參數(shù)就是計算過的對象,即便這時還沒有渲染,得到的依然是符合直覺的計算過的值。專題一覽什么是可變狀態(tài)不可變屬性生命周期組件事件操作抽象 本文是『horseshoe·React專題』系列文章之一,后續(xù)會有更多專題推出來我的 GitHub repo 閱讀完整的專題文章來我的 個人博客 獲得無與倫比的閱讀體驗 Reac...
摘要:另外本文中會介紹一個通過類繼承方式定義的組件的生命周期,以及在各個生命周期函數(shù)中能做什么,不能或盡量不要做什么。各個生命周期函數(shù)介紹及使用經(jīng)驗。獲取組件的初始內(nèi)部狀態(tài)在中。該聲明周期函數(shù)可能在兩種情況下被調(diào)用組件接收到了新的屬性。 文章標(biāo)題總算是可以正常一點了…… 通過之前的文章我們已經(jīng)知道:在 React 體系中所謂的 在 JavaScript 中編寫 HTML 代碼 指的是 Rea...
閱讀 2683·2021-11-18 10:02
閱讀 2310·2021-09-30 09:47
閱讀 1842·2021-09-27 14:01
閱讀 3154·2021-08-16 11:00
閱讀 3196·2019-08-30 11:06
閱讀 2424·2019-08-29 17:29
閱讀 1563·2019-08-29 13:19
閱讀 470·2019-08-26 13:54