最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

react時(shí)間分片實(shí)現(xiàn)流程詳解

 更新時(shí)間:2022年11月25日 14:34:29   作者:flyzz177  
實(shí)現(xiàn)react時(shí)間分片,主要內(nèi)容包括什么是時(shí)間分片、為什么需要時(shí)間分片、實(shí)現(xiàn)分片開啟 - 固定、實(shí)現(xiàn)分片中斷、重啟 - 連續(xù)、分片重啟、實(shí)現(xiàn)延遲執(zhí)行 - 有間隔、時(shí)間分片異步執(zhí)行方案的演進(jìn)、時(shí)間分片簡(jiǎn)單實(shí)現(xiàn)、總結(jié)、基本概念、基礎(chǔ)應(yīng)用、原理機(jī)制和需要注意的事項(xiàng)等

我們常說(shuō)的調(diào)度,可以分為兩大模塊,時(shí)間分片和優(yōu)先級(jí)調(diào)度

  • 時(shí)間分片的異步渲染是優(yōu)先級(jí)調(diào)度實(shí)現(xiàn)的前提
  • 優(yōu)先級(jí)調(diào)度在異步渲染的基礎(chǔ)上引入優(yōu)先級(jí)機(jī)制控制任務(wù)的打斷、替換。

本節(jié)將從時(shí)間分片的實(shí)現(xiàn)剖析react的異步渲染原理,閱讀本文你講可以了解

  • 時(shí)間分片是什么
  • 為什么需要時(shí)間分片
  • 時(shí)間分片在react中是如何運(yùn)行的
  • 時(shí)間分片的極簡(jiǎn)實(shí)現(xiàn)

什么是時(shí)間分片

上文提到過(guò),時(shí)間分片其實(shí)就是一個(gè)固定而連續(xù)且有間隔的時(shí)間區(qū)間

固定:時(shí)間分片是工作時(shí)長(zhǎng)是固定的
連續(xù):分片之間是連續(xù)的,當(dāng)前分片內(nèi)有工作沒(méi)做完,會(huì)留到下個(gè)分片繼續(xù)
有間隔:在進(jìn)入下一個(gè)分片前,會(huì)有一定時(shí)間的間隔

這些解釋比較抽象,可以更加通俗去理解

固定:每天固定工作8小時(shí)
連續(xù):每天都要上班
有間隔:明天上班前會(huì)休息一段時(shí)間

為什么需要時(shí)間分片

我們知道,react最重要,也是最耗時(shí)的任務(wù)是節(jié)點(diǎn)遍歷。

設(shè)想一個(gè)頁(yè)面上有一萬(wàn)個(gè)DOM節(jié)點(diǎn),如果我們用同步的方式一個(gè)個(gè)遍歷完需要花費(fèi)多少時(shí)間。而且如果是同步遍歷的話,遍歷的過(guò)程中,JS線程一直會(huì)霸占主線程,導(dǎo)致阻塞了瀏覽器的其他線程,導(dǎo)致卡頓的情況出現(xiàn)。

換個(gè)思路解決這個(gè)遍歷問(wèn)題,能不能遍歷一會(huì),休息一會(huì),休息的過(guò)程中就可以把主線程交還給渲染線程和事件線程,這樣就能及時(shí)渲染節(jié)點(diǎn)和響應(yīng)用戶事件,避免造成卡頓。

為了實(shí)現(xiàn)遍歷一會(huì),休息一會(huì),我們可以將整個(gè)過(guò)程分解為以下三個(gè)步驟

  • 分片開啟
  • 分片中斷、分片重啟
  • 延遲執(zhí)行

這三個(gè)步驟與時(shí)間分片的三個(gè)特性一一對(duì)應(yīng)

實(shí)現(xiàn)分片開啟 - 固定

時(shí)間分片是獨(dú)立于React的節(jié)點(diǎn)遍歷流程的,所以只需要把節(jié)點(diǎn)遍歷的入口函數(shù)以回調(diào)函數(shù)的形式傳入即可,這樣就可以讓時(shí)間分片來(lái)決定節(jié)點(diǎn)遍歷執(zhí)行時(shí)機(jī)。

// 節(jié)點(diǎn)遍歷的入口函數(shù)
function Reconcile協(xié)調(diào)() {
    節(jié)點(diǎn)遍歷()
}
function Schedule調(diào)度() {
    創(chuàng)建分片(Reconcile協(xié)調(diào))
}

第一步,需要將時(shí)間分片要調(diào)度的函數(shù)抽象為一個(gè)任務(wù)對(duì)象

function 創(chuàng)建分片(需要被調(diào)度的函數(shù)) {
    const 新的任務(wù) = {
        callback: 需要被調(diào)度的函數(shù)
    }
}

第二步,設(shè)定分片工作時(shí)長(zhǎng),為了方便后續(xù),可以直接計(jì)算過(guò)期時(shí)間。分片工作時(shí)長(zhǎng)一般為5ms,但Scheduler會(huì)根據(jù)任務(wù)優(yōu)先級(jí)有所調(diào)整,這里為了更好理解,先默認(rèn)5ms。

const taskQueue = []
function 創(chuàng)建分片(需要被調(diào)度的函數(shù)) {
    const 新的任務(wù) = {
        callback: 需要被調(diào)度的函數(shù),
        expirationTime: performance.now() + 5000
    }
    taskQueue.push(新的任務(wù))
    發(fā)起異步調(diào)度()
}

每次分片的創(chuàng)建其實(shí)都是新一輪調(diào)度的開始,所以在末尾會(huì)發(fā)起異步調(diào)度

為什么用performance.now()而不用Date.now()

performance.now()返回當(dāng)前頁(yè)面的停留時(shí)間,Date.now()返回當(dāng)前系統(tǒng)時(shí)間。但不同的是performance.now()精度更高,且比Date.now()更可靠

  • performance.now()返回的是微秒級(jí)的,Date.now()只是毫秒級(jí)
  • performance.now()一個(gè)恒定的速率慢慢增加的,它不會(huì)受到系統(tǒng)時(shí)間的影響。Date.now()受到系統(tǒng)時(shí)間影響,系統(tǒng)時(shí)間修改Date.now()也會(huì)改變

實(shí)現(xiàn)分片中斷、重啟 - 連續(xù)

分片中斷

我們?cè)诘谝徽乱呀?jīng)將React的虛擬DOM結(jié)構(gòu)從樹形結(jié)構(gòu)優(yōu)化成鏈表結(jié)構(gòu),所以能輕松使用while循環(huán)實(shí)現(xiàn)可中斷的遍歷

那么如果要將遍歷任務(wù)時(shí)間分片相結(jié)合,且實(shí)現(xiàn)分片中斷功能的話,只需要在while循環(huán)出加入分片時(shí)間過(guò)期的校驗(yàn)即可

function 分片過(guò)期校驗(yàn)() {
    return (perfromance.now() - 分片開啟時(shí)間) >= 5000
}
let 需要被遍歷的幸運(yùn)兒節(jié)點(diǎn) = null
function 構(gòu)建節(jié)點(diǎn)() {
    /** * ...在這里進(jìn)行節(jié)點(diǎn)構(gòu)建工作 */
    需要被遍歷的幸運(yùn)兒節(jié)點(diǎn) = 需要被遍歷的幸運(yùn)兒節(jié)點(diǎn).next
}
function 節(jié)點(diǎn)遍歷() {
    while (需要被遍歷的幸運(yùn)兒節(jié)點(diǎn) != null && !分片過(guò)期校驗(yàn)()) {
        構(gòu)建節(jié)點(diǎn)()
    }
}
function Schedule調(diào)度() {
    創(chuàng)建分片(Reconcile協(xié)調(diào))
}

分片重啟

分片重啟意思就是上一輪時(shí)間分片因?yàn)檫^(guò)期中斷了,需要重新發(fā)起一輪時(shí)間分片。

實(shí)現(xiàn)的思路是,在上一輪分片結(jié)束之后判斷是否還需要開啟下一輪分片,需要的話則重新發(fā)起一輪異步調(diào)度即可,相關(guān)參考視頻講解:進(jìn)入學(xué)習(xí)

function 分片過(guò)期校驗(yàn)() {
    return (perfromance.now() - 分片開啟時(shí)間) >= 5000
}
function 分片事件循環(huán)() {
    let 棧頂任務(wù) = taskQueue.peek()

    while (棧頂任務(wù)) {
        if (分片過(guò)期校驗(yàn)()) break
        const 棧頂任務(wù)回調(diào) = 棧頂任務(wù).callback()
        if (typeof 棧頂任務(wù)回調(diào) == 'function') {
            // 當(dāng)前任務(wù)還沒(méi)有執(zhí)行完,繼續(xù)搞
            棧頂任務(wù).callback = 棧頂任務(wù)回調(diào)
        } else {
            // 當(dāng)前任務(wù)已執(zhí)行完,彈出隊(duì)列
            taskQueue.pop()
        }
        棧頂任務(wù) = taskQueue.peek()
    }
    // 還有任務(wù)哦
    if (棧頂任務(wù)) return true
    return false
}
function 分片執(zhí)行() {
    分片開啟時(shí)間 = performance.now()
    var 是否還有任務(wù)未執(zhí)行完畢
    try {
        是否還有任務(wù)未執(zhí)行完畢 = 分片事件循環(huán)()
    } finally {
        // 分片重啟
        if (是否還有任務(wù)未執(zhí)行) 發(fā)起異步調(diào)度()
    }
}
function 發(fā)起異步調(diào)度() {
    // 這里實(shí)際上是異步執(zhí)行,看下面有間隔
    分片執(zhí)行()
}

重啟的條件就是判斷分片任務(wù)隊(duì)列中是否還有任務(wù),有的話就發(fā)起下一輪的時(shí)間分片

實(shí)現(xiàn)延遲執(zhí)行 - 有間隔

有間隔的本質(zhì)是延遲JS的執(zhí)行,讓瀏覽器有喘息的時(shí)間,去處理其他線程的任務(wù),哪如何把主線程控制權(quán)交還給瀏覽器呢??

可以使用異步特性發(fā)起下一輪時(shí)間分片,實(shí)現(xiàn)延遲執(zhí)行

function 發(fā)起異步調(diào)度() {
    // 將主線程短暫的交還給瀏覽器
    setTimeout(() => {
        分片執(zhí)行()
    }, 0)
}

為什么選擇宏任務(wù)實(shí)現(xiàn)異步執(zhí)行

微任務(wù)無(wú)法真正達(dá)到交還主線程控制權(quán)的要求。

因?yàn)橐惠喪录h(huán),是先執(zhí)行一個(gè)宏任務(wù),然后再清空微任務(wù)隊(duì)列里面的任務(wù),如果在清空微任務(wù)隊(duì)列的過(guò)程中,依然有新任務(wù)插入到微任務(wù)隊(duì)列中的話,還是把這些任務(wù)執(zhí)行完畢才會(huì)釋放主線程。所以微任務(wù)不合適。

時(shí)間分片異步執(zhí)行方案的演進(jìn)

為什么不是setTimeout?

因?yàn)閟etTimeout的遞歸層級(jí)過(guò)深的話,延遲就不是1ms,而是4ms,這樣會(huì)造成延遲時(shí)間過(guò)長(zhǎng)

為什么不是requestAnimationFrame?

requestAnimationFramed是在微任務(wù)執(zhí)行完之后,瀏覽器重排重繪之前執(zhí)行,執(zhí)行的時(shí)機(jī)是不準(zhǔn)確的。如果raf之前JS的執(zhí)行時(shí)間過(guò)長(zhǎng),依然會(huì)造成延遲

為什么不是requestIdleCallback?

requestIdleCallback的執(zhí)行時(shí)機(jī)是在瀏覽器重排重繪之后,也就是瀏覽器的空閑時(shí)間執(zhí)行。其實(shí)執(zhí)行的時(shí)機(jī)依然是不準(zhǔn)確的,raf執(zhí)行的JS代碼耗時(shí)可能會(huì)過(guò)長(zhǎng)

為什么是 MessageChannel?

MessageChannel的執(zhí)行時(shí)機(jī)比setTimeout靠前

在React中,異步執(zhí)行優(yōu)先使用setImmediate,其次是MessageChannel,最后是setTimeout,都是根據(jù)瀏覽器對(duì)這些的特性支持程度決定的。

時(shí)間分片簡(jiǎn)單實(shí)現(xiàn)

下面會(huì)整合上面的所有代碼,模擬出最簡(jiǎn)單的時(shí)間分片實(shí)現(xiàn)(不包含優(yōu)先級(jí)機(jī)制)

Scheduler.js

const taskQueue = []
let 分片開啟時(shí)間 = -1
// **時(shí)間分片核心**
const 分片過(guò)期校驗(yàn) = () => {
    return (perfromance.now() - 分片開啟時(shí)間) >= 5000
}
function 分片事件循環(huán)() {
    let 棧頂任務(wù) = taskQueue.peek()
    while (棧頂任務(wù)) {
        // 每執(zhí)行完一個(gè)任務(wù),都要校驗(yàn)一下分片是否過(guò)期
        if (分片過(guò)期校驗(yàn)()) break
        const 棧頂任務(wù)回調(diào) = 棧頂任務(wù).callback()
        if (typeof 棧頂任務(wù)回調(diào) == 'function') {
            // 當(dāng)前任務(wù)還沒(méi)有執(zhí)行完,繼續(xù)搞
            棧頂任務(wù).callback = 棧頂任務(wù)回調(diào)
        } else {
            // 當(dāng)前任務(wù)已執(zhí)行完,彈出隊(duì)列
            taskQueue.pop()
        }
        棧頂任務(wù) = taskQueue.peek()
    }
    // 還有任務(wù)哦
    if (棧頂任務(wù)) return true
    return false
}
function 分片執(zhí)行() {
    分片開啟時(shí)間 = performance.now()
    var 是否還有任務(wù)未執(zhí)行完畢
    try {
        是否還有任務(wù)未執(zhí)行完畢 = 分片事件循環(huán)()
    } finally {
        // **時(shí)間分片核心:分片重啟**
        if (是否還有任務(wù)未執(zhí)行) 發(fā)起異步調(diào)度()
    }
}
// 實(shí)例化 MessageChannel
const channel = new MessageChannel()
const port2 = channel.port2
channel.port1.onmessage = 分片執(zhí)行
function 發(fā)起異步調(diào)度() {
    // 向通道1發(fā)消息,通道1收到消息就會(huì)執(zhí)行分片任務(wù)
    // **時(shí)間分片核心:延遲執(zhí)行**
    port2.postMessage(null)
}
function 創(chuàng)建分片(需要被調(diào)度的函數(shù)) {
    // **時(shí)間分片核心:分片開啟**
    const 新的任務(wù) = {
        callback: 需要被調(diào)度的函數(shù),
        expirationTime: performance.now() + 5000
    }
    taskQueue.push(新的任務(wù))
    發(fā)起異步調(diào)度()
}
export default {
    創(chuàng)建分片,
    分片過(guò)期校驗(yàn)
}

ReactDOM.js

import * as Scheduler from './Scheduler'
const {
    創(chuàng)建分片,
    分片過(guò)期校驗(yàn)
} = Scheduler
let 需要被遍歷的幸運(yùn)兒節(jié)點(diǎn) = null
function 構(gòu)建節(jié)點(diǎn)() {
    /** * ...在這里進(jìn)行節(jié)點(diǎn)構(gòu)建工作 */
    需要被遍歷的幸運(yùn)兒節(jié)點(diǎn) = 需要被遍歷的幸運(yùn)兒節(jié)點(diǎn).next
}
function 節(jié)點(diǎn)遍歷() {
    // **時(shí)間分片核心:分片中斷**
    while (需要被遍歷的幸運(yùn)兒節(jié)點(diǎn) != null && !分片過(guò)期校驗(yàn)()) {
        構(gòu)建節(jié)點(diǎn)()
    }
}
function Schedule調(diào)度() {
    創(chuàng)建分片(Reconcile協(xié)調(diào))
}
function 調(diào)度入口() {
    需要被遍歷的幸運(yùn)兒節(jié)點(diǎn) = react應(yīng)用根節(jié)點(diǎn)
    Schedule調(diào)度()
}
調(diào)度入口()

這段時(shí)間分片的偽代碼相對(duì)于react中源碼的實(shí)現(xiàn),少了很多邏輯判斷,并且集中了起來(lái),應(yīng)該會(huì)相對(duì)好理解很多。

如果還是覺(jué)得有點(diǎn)晦澀,可以重點(diǎn)關(guān)注偽代碼中標(biāo)有時(shí)間分片核心注釋的代碼,結(jié)合上文提到的概念理解

總結(jié)

讀完這篇文章估計(jì)你可能對(duì)時(shí)間分片的概念已經(jīng)有所有了解了,是不是覺(jué)得react16的新特性之一時(shí)間分片,也并沒(méi)有想象中的神秘。

總的下來(lái),時(shí)間分片就是由簡(jiǎn)單的三個(gè)模塊組成:

  • 分片開啟
  • 分片中斷、重啟
  • 延遲執(zhí)行

時(shí)間分片是Scheduler調(diào)度器兩大特性中的一個(gè),另一個(gè)是任務(wù)的優(yōu)先級(jí)調(diào)度,接下來(lái)可能會(huì)花兩到三篇的篇幅去講解。在源碼閱讀的過(guò)程中,我覺(jué)得時(shí)間分片的實(shí)現(xiàn)已經(jīng)非常驚艷了,沒(méi)想到后面優(yōu)先級(jí)調(diào)度的設(shè)計(jì)對(duì)我更是無(wú)可匹敵的沖擊。

到此這篇關(guān)于react時(shí)間分片實(shí)現(xiàn)流程詳解的文章就介紹到這了,更多相關(guān)react時(shí)間分片內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • React實(shí)現(xiàn)核心Diff算法的示例代碼

    React實(shí)現(xiàn)核心Diff算法的示例代碼

    這篇文章主要為大家詳細(xì)介紹了React如何實(shí)現(xiàn)Diff算法,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來(lái)幫助
    2022-04-04
  • React中引入less、less-loader問(wèn)題

    React中引入less、less-loader問(wèn)題

    這篇文章主要介紹了React中引入less、less-loader問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-01-01
  • react Input組件Compositionstart和Compositionend事件

    react Input組件Compositionstart和Compositionend事件

    這篇文章主要為大家介紹了Compositionstart和Compositionend事件之于react組件庫(kù)Input組件的坑解決,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-11-11
  • React處理復(fù)雜圖片樣式的方法詳解

    React處理復(fù)雜圖片樣式的方法詳解

    這篇文章主要為大家詳細(xì)介紹了React處理復(fù)雜圖片樣式的方法,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2024-04-04
  • React中如何處理承諾demo

    React中如何處理承諾demo

    這篇文章主要為大家介紹了React中如何處理承諾demo,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-12-12
  • 淺談react路由傳參的幾種方式

    淺談react路由傳參的幾種方式

    這篇文章主要介紹了淺談react路由傳參的幾種方式,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-03-03
  • 關(guān)于react中組件通信的幾種方式詳解

    關(guān)于react中組件通信的幾種方式詳解

    這篇文章主要給大家介紹了關(guān)于react中組件通信的幾種方式,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。
    2017-12-12
  • React中使用Redux Toolkit狀態(tài)管理的示例詳解

    React中使用Redux Toolkit狀態(tài)管理的示例詳解

    在現(xiàn)代 React 應(yīng)用程序中,狀態(tài)管理是一個(gè)至關(guān)重要的部分,使用 Redux Toolkit 可以簡(jiǎn)化 Redux 的配置和管理,本文將通過(guò)三個(gè)文件的示例,詳細(xì)講解如何使用 Redux Toolkit 創(chuàng)建和管理一個(gè)簡(jiǎn)單的計(jì)數(shù)器狀態(tài),需要的朋友可以參考下
    2024-11-11
  • React文件名和目錄規(guī)范最佳實(shí)踐記錄(總結(jié)篇)

    React文件名和目錄規(guī)范最佳實(shí)踐記錄(總結(jié)篇)

    React在使用時(shí)非常靈活,如果沒(méi)有一個(gè)規(guī)范約束項(xiàng)目,在開發(fā)過(guò)程中會(huì)非?;靵y,本文將介紹幾個(gè)優(yōu)秀的規(guī)范,介紹文件名和目錄前,需要先簡(jiǎn)述一下幾種通用的類型,用來(lái)區(qū)分文件的功能,感興趣的朋友一起看看吧
    2022-05-05
  • react源碼層探究setState作用

    react源碼層探究setState作用

    寫react的時(shí)候,踩了幾次坑發(fā)現(xiàn)setstate之后state不會(huì)立刻更新,于是判定setstate就是異步的方法,但是直到有一天,我想立刻拿到更新的state去傳參另一個(gè)方法的時(shí)候,才問(wèn)自己,為什么setstate是異步的?準(zhǔn)確地說(shuō),在React內(nèi)部機(jī)制能檢測(cè)到的地方,setState就是異步的
    2022-10-10

最新評(píng)論

上犹县| 保德县| 镇赉县| 台北县| 克东县| 龙泉市| 凤台县| 来安县| 锡林郭勒盟| 伊吾县| 大渡口区| 博野县| 蓬莱市| 临沭县| 海宁市| 通榆县| 迁安市| 定西市| 茌平县| 慈利县| 南昌市| 达尔| 汉源县| 成武县| 长治县| 龙州县| 东莞市| 辽宁省| 平凉市| 浦江县| 林西县| 怀集县| 海门市| 扶绥县| 改则县| 海宁市| 中方县| 宝坻区| 驻马店市| 阿图什市| 武冈市|