做高效能的父母網(wǎng)站金華seo扣費(fèi)
時(shí)間分片
- react的任務(wù)可以被打斷,其實(shí)就是基于時(shí)間分片的
- 人眼最高能識(shí)別的幀數(shù)不超過30幀,電影的幀數(shù)差不多是在24
- 瀏覽器的幀率一般來說是60幀,也就是每秒60個(gè)畫面, 平均一個(gè)畫面大概是16.5毫秒左右
- 瀏覽器正常的工作流程是運(yùn)算渲染,運(yùn)算,渲染運(yùn)算渲染
- 在瀏覽器里面一個(gè)運(yùn)算,加上一個(gè)渲染就是一幀
- 總的來講,可以理解為下面這張圖

- 比如 frame 是一幀,一個(gè) Frame 就是16毫秒左右
- 黑色部分是瀏覽器的渲染,藍(lán)色部分是js的運(yùn)算
- 在16毫秒以內(nèi)(一幀), 瀏覽器會(huì)重新渲染畫面,然后再加上JS的一輪事件循環(huán)的執(zhí)行
- 根據(jù)任務(wù)隊(duì)列循環(huán)下去,一秒 60 幀,每一幀都是 js的執(zhí)行 + 瀏覽器的渲染
- 但是, js它是單線程的, 會(huì)阻塞瀏覽器渲染, 假如 js執(zhí)行時(shí)間超長,占了 3 ~ 4幀
- js執(zhí)行的時(shí)候,瀏覽器是不能渲染的,那這個(gè)時(shí)候會(huì)有頁面卡頓的感覺
- 實(shí)際上這個(gè)時(shí)候是 js 在執(zhí)行
- 這個(gè)也是react它去遞歸渲染的時(shí)候的問題
- 遞歸渲染,它就是屬于長進(jìn)程,相當(dāng)于在 render 的時(shí)候 js 一直把渲染進(jìn)程給卡住
- 這個(gè)是很苦惱的問題,所以誕生了fiber架構(gòu), react希望能夠把任務(wù)分片處理
- 這個(gè)時(shí)候就提到了一個(gè)概念,就是 fiber reconciler 要做的事情
- 它如何讓我們把時(shí)間分片,然后讓又讓瀏覽器不卡頓的呢?
- 其實(shí)特別的巧妙,谷歌瀏覽器底層提供的一個(gè)東西叫做 requestIdleCallback
- 前面說到一幀(16ms左右) 是 渲染 + js的執(zhí)行
- 有時(shí)候?yàn)g覽器比較空閑,有可能一幀不需要 16ms,可能需要6ms, 那剩下的10ms可以執(zhí)行長任務(wù)
- 當(dāng)剩下的10ms用完,可以把瀏覽器的渲染權(quán)利再還給瀏覽器
- 這個(gè)時(shí)候進(jìn)入下一幀的瀏覽器的畫面,繼續(xù)渲染,渲染完之后又有剩余時(shí)間
- 接著再執(zhí)行這個(gè)長進(jìn)程,簡單來說,就是把長進(jìn)程拆分成一個(gè)個(gè)很小的任務(wù)
- 它利用瀏覽器每一幀的空閑時(shí)間去執(zhí)行,這樣就實(shí)現(xiàn)了任務(wù)的打斷,而且還不阻塞瀏覽器的渲染
- 也就是說,本來一個(gè)任務(wù)要執(zhí)行1秒,但是實(shí)際上react的fiber架構(gòu)可能讓這個(gè)1秒執(zhí)行的時(shí)間更長
- 因?yàn)槿蝿?wù)的拆分其實(shí)是增加了這個(gè)計(jì)算的開銷的,但是,它卻是在我們每一幀的空閑時(shí)間去執(zhí)行的
- 雖然執(zhí)行的整體時(shí)間可能變長,但是讓用戶的感覺沒有那么卡頓,所以它的體驗(yàn)是提升了的
- 參考之前 React 16的時(shí)間片:https://blog.csdn.net/Tyro_java/article/details/135586572
關(guān)于 requestIdleCallback
-
文檔:https://developer.mozilla.org/zh-CN/docs/Web/API/Window/requestIdleCallback
-
window.requestIdleCallback() 方法插入一個(gè)函數(shù),這個(gè)函數(shù)將在瀏覽器空閑時(shí)期被調(diào)用
-
這使開發(fā)者能夠在主事件循環(huán)上執(zhí)行后臺(tái)和低優(yōu)先級(jí)工作,而不會(huì)影響延遲關(guān)鍵事件,如動(dòng)畫和輸入響應(yīng)
-
函數(shù)一般會(huì)按先進(jìn)先調(diào)用的順序執(zhí)行,然而,如果回調(diào)函數(shù)指定了執(zhí)行超時(shí)時(shí)間timeout,則有可能為了在超時(shí)前執(zhí)行函數(shù)而打亂執(zhí)行順序
-
requestIdleCallback(callback)
-
requestIdleCallback(callback, options)
- callback
- 一個(gè)在事件循環(huán)空閑時(shí)即將被調(diào)用的函數(shù)的引用。函數(shù)會(huì)接收到一個(gè)名為 IdleDeadline 的參數(shù)
- 這個(gè)參數(shù)可以獲取當(dāng)前空閑時(shí)間以及回調(diào)是否在超時(shí)時(shí)間前已經(jīng)執(zhí)行的狀態(tài)
- options 可選
- 包括可選的配置參數(shù)。具有如下屬性
- timeout
- 如果指定了 timeout,并且有一個(gè)正值,而回調(diào)在 timeout 毫秒過后還沒有被調(diào)用
- 那么回調(diào)任務(wù)將放入事件循環(huán)中排隊(duì),即使這樣做有可能對性能產(chǎn)生負(fù)面影響
- timeout
- 包括可選的配置參數(shù)。具有如下屬性
- callback
-
返回值是一個(gè)ID,可以把它傳入
Window.cancelIdleCallback()
方法來結(jié)束回調(diào)
requestIdleCallback 和 requestAnimationFrame 的區(qū)別
1 )react fiber 引起的關(guān)注
- 組件樹轉(zhuǎn)換為鏈表,可分段渲染
- 渲染時(shí)可以暫停,去執(zhí)行其他高優(yōu)先任務(wù),空閑時(shí)再繼續(xù)渲染
- 如何判斷空閑?requestIdleCallback
2 ) 區(qū)別
-
requestAnimationFrame 每次渲染完都會(huì)執(zhí)行,高優(yōu)
-
requestIdleCallback 空閑時(shí)才會(huì)執(zhí)行,低優(yōu)
let curWidth = 100 const maxWidth = 400function addWidth() {curWidth = curWidth + 3box.style.width = `${curWidth} px`if (curWidth < maxWidth) {widndow.requestAnimationFrame(addWidth) // 時(shí)間不用自己控制 高優(yōu)先級(jí)widndow.requestIdleCallback(addWidth) // 時(shí)間不用自己控制 繁忙時(shí)不會(huì)執(zhí)行} }addWidth()
-
對比
console.info('start') window.requestIdleCallback(()=>{console.log('requestIdleCallback') }) window.requestAnimationFrame(()=>{console.log('requestAnimationFrame') }) setTimeout(()=>{console.log('setTimeout') }) console.info('end')
-
執(zhí)行順序
- start
- end
- timeout 優(yōu)先級(jí)更高
- requestAnimationFrame 宏任務(wù)優(yōu)先級(jí)較高
- requestIdleCallback 宏任務(wù)優(yōu)先級(jí)較低
-
總結(jié)
- 兩者都是宏任務(wù)
- 需要等待dom渲染完才會(huì)執(zhí)行