React不使用requestIdleCallback實(shí)現(xiàn)調(diào)度原理解析
1.起因
最近在一邊啃源碼,一邊手寫fiber嘛,然后也看了很多博客和資料,基本上大伙好像都是說用requestIdleCallback來模擬react實(shí)現(xiàn)一個(gè)空閑時(shí)間調(diào)度。但我自己手寫的時(shí)候把怎么用怎么怪,老是感覺有什么地方不對(duì)勁而且是在調(diào)度過程中,可能是因?yàn)槲沂窍雽懗鰜韥硪粋€(gè)相對(duì)健全一點(diǎn)的模版方便我以后寫源碼的其他部分把,然后分析了一下所以有了這篇博客。
2.查找問題
1.requestIdleCallback是利用幀之間空閑時(shí)間來執(zhí)行JS,它是一個(gè)低優(yōu)先級(jí)的處理策略它給我的感覺就是做一些類似上報(bào)之類的操作,但實(shí)際上Fiber的構(gòu)建以及渲染內(nèi)容,并不算是一個(gè)低優(yōu)先級(jí)任務(wù)。
2.兼容性這個(gè)就總所周知了,這個(gè)api并不適合在生產(chǎn)環(huán)境上。
3.requestIdleCallback實(shí)際上是在布局和繪制之后,那意味在也許你在里面做的事情(可能是通過數(shù)據(jù)修改觸發(fā)dom修改)會(huì)重排。可以看看這個(gè)試驗(yàn)
3.解決問題
所以這時(shí)候我們就可以回到源碼中去看,react是怎么實(shí)現(xiàn)的,源碼地址。
核心調(diào)度實(shí)現(xiàn)
// 有執(zhí)行任務(wù) if (scheduledHostCallback !== null) { const currentTime = getCurrentTime(); // 計(jì)算一幀的過期時(shí)間點(diǎn) deadline = currentTime + yieldInterval; const hasTimeRemaining = true; try { // 執(zhí)行c回調(diào) const hasMoreWork = scheduledHostCallback( hasTimeRemaining, currentTime, ); // 執(zhí)行完該回調(diào)后, 判斷后續(xù)是否還有其他任務(wù) if (!hasMoreWork) { isMessageLoopRunning = false; scheduledHostCallback = null; } else { // 還有其他任務(wù), 推進(jìn)進(jìn)入下一個(gè)宏任務(wù)隊(duì)列中 port.postMessage(null); } } catch (error) { // If a scheduler task throws, exit the current browser task so the // error can be observed. port.postMessage(null); throw error; } } else { isMessageLoopRunning = false; } // Yielding to the browser will give it a chance to paint, so we can // reset this. // 重置狀態(tài) needsPaint = false; }; const channel = new MessageChannel(); // port2 發(fā)送 const port = channel.port2; // port1 接收 channel.port1.onmessage = performWorkUntilDeadline; // 在每一幀中執(zhí)行任務(wù) requestHostCallback = function(callback) { // 回調(diào)注冊(cè) scheduledHostCallback = callback; if (!isMessageLoopRunning) { isMessageLoopRunning = true; // 進(jìn)入宏任務(wù)隊(duì)列 port.postMessage(null); } }; // 取消回調(diào) cancelHostCallback = function() { scheduledHostCallback = null; }; // 設(shè)置超時(shí)回調(diào) requestHostTimeout = function(callback, ms) { taskTimeoutID = setTimeout(() => { callback(getCurrentTime()); }, ms); }; // 取消超時(shí) cancelHostTimeout = function() { clearTimeout(taskTimeoutID); taskTimeoutID = -1; };
代碼里的注釋寫得很清楚了把,但有幾個(gè)點(diǎn)可以說一下。
1.首先選擇宏任務(wù),因?yàn)槲覀冃枰ゼ皶r(shí)的讓出主線程(微任務(wù)并不會(huì)讓出主線程也是在更新頁(yè)面前去執(zhí)行)。
2.其次是宏任務(wù)中的選擇,MessageChannel,setTimeout,requestAnimationFrame,都是宏任務(wù),setTimeout會(huì)浪費(fèi)4ms(這個(gè)大伙可以去看看),requestAnimationFrame的觸發(fā)時(shí)間是不穩(wěn)定的(可以看看瀏覽器的更新頁(yè)面機(jī)制),所以我猜想最后就選了MessageChannel把。
4.總結(jié)
其實(shí)到這思路也比較明了了,把React中為什么不使用requestIdleCallback理清楚,還順便把React的核心調(diào)度原理看了一下。
5.吐槽
唉,其實(shí)看源碼和手寫源碼完全是兩種感覺,更多的是體現(xiàn)在實(shí)現(xiàn)細(xì)節(jié)和代碼耦合性健壯性的問題,寫是怎么寫都行,但如何寫優(yōu)雅的方便人迭代的代碼就好燒腦。比如你就想實(shí)現(xiàn)一個(gè)fiber的大體思路就不難,但是如果想你在fiber上加hook,難度幾何飆升,基礎(chǔ)構(gòu)建和細(xì)節(jié)實(shí)現(xiàn)就很重要了,手寫肯定是不等于抄,還需要在里面加寫自己的想法和如何簡(jiǎn)化的方案。
以上就是React不使用requestIdleCallback實(shí)現(xiàn)調(diào)度原理解析的詳細(xì)內(nèi)容,更多關(guān)于React不使用requestIdleCallback調(diào)度的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
React類組件和函數(shù)組件對(duì)比-Hooks的簡(jiǎn)介
Hook?是?React?16.8?的新增特性,它可以讓我們?cè)诓痪帉慶lass的情況下,?使用state以及其他的React特性(比如生命周期,這篇文章主要介紹了React類組件和函數(shù)組件對(duì)比-Hooks的介紹及初體驗(yàn),需要的朋友可以參考下2022-11-11react.js實(shí)現(xiàn)頁(yè)面登錄跳轉(zhuǎn)示例
本文主要介紹了react.js實(shí)現(xiàn)頁(yè)面登錄跳轉(zhuǎn)示例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-01-01react antd checkbox實(shí)現(xiàn)全選、多選功能
目前好像只有table組件有實(shí)現(xiàn)表格數(shù)據(jù)的全選功能,如果說對(duì)于list,card,collapse等其他組件來說,需要自己結(jié)合checkbox來手動(dòng)實(shí)現(xiàn)全選功能,這篇文章主要介紹了react antd checkbox實(shí)現(xiàn)全選、多選功能,需要的朋友可以參考下2024-07-07詳解超簡(jiǎn)單的react服務(wù)器渲染(ssr)入坑指南
這篇文章主要介紹了詳解超簡(jiǎn)單的react服務(wù)器渲染(ssr)入坑指南,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2019-02-02解決React?hook?'useState'?cannot?be?called?in?
這篇文章主要為大家介紹了React?hook?'useState'?cannot?be?called?in?a?class?component報(bào)錯(cuò)解決方法,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-12-12React實(shí)現(xiàn)控制減少useContext導(dǎo)致非必要的渲染詳解
這篇文章主要介紹了React如何有效減少使用useContext導(dǎo)致的不必要渲染,使用useContext在改變一個(gè)數(shù)據(jù)時(shí),是通過自己逐級(jí)查找對(duì)比改變的數(shù)據(jù)然后渲染,本文通過示例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-11-11