日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學(xué)無先后,達(dá)者為師

網(wǎng)站首頁 編程語言 正文

React不使用requestIdleCallback實現(xiàn)調(diào)度原理解析_React

作者:用戶6816925221619 ? 更新時間: 2022-12-25 編程語言

1.起因

最近在一邊啃源碼,一邊手寫fiber嘛,然后也看了很多博客和資料,基本上大伙好像都是說用requestIdleCallback來模擬react實現(xiàn)一個空閑時間調(diào)度。但我自己手寫的時候把怎么用怎么怪,老是感覺有什么地方不對勁而且是在調(diào)度過程中,可能是因為我是想寫出來來一個相對健全一點的模版方便我以后寫源碼的其他部分把,然后分析了一下所以有了這篇博客。

2.查找問題

1.requestIdleCallback是利用幀之間空閑時間來執(zhí)行JS,它是一個低優(yōu)先級的處理策略它給我的感覺就是做一些類似上報之類的操作,但實際上Fiber的構(gòu)建以及渲染內(nèi)容,并不算是一個低優(yōu)先級任務(wù)。

2.兼容性這個就總所周知了,這個api并不適合在生產(chǎn)環(huán)境上。

3.requestIdleCallback實際上是在布局和繪制之后,那意味在也許你在里面做的事情(可能是通過數(shù)據(jù)修改觸發(fā)dom修改)會重排。可以看看這個試驗

3.解決問題

所以這時候我們就可以回到源碼中去看,react是怎么實現(xiàn)的,源碼地址。

核心調(diào)度實現(xiàn)

    // 有執(zhí)行任務(wù)
    if (scheduledHostCallback !== null) {
      const currentTime = getCurrentTime();
      // 計算一幀的過期時間點
      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)入下一個宏任務(wù)隊列中
          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)注冊
    scheduledHostCallback = callback;
    if (!isMessageLoopRunning) {
      isMessageLoopRunning = true;
      // 進(jìn)入宏任務(wù)隊列
      port.postMessage(null);
    }
  };
  // 取消回調(diào)
  cancelHostCallback = function() {
    scheduledHostCallback = null;
  };
  // 設(shè)置超時回調(diào)
  requestHostTimeout = function(callback, ms) {
    taskTimeoutID = setTimeout(() => {
      callback(getCurrentTime());
    }, ms);
  };
  // 取消超時
  cancelHostTimeout = function() {
    clearTimeout(taskTimeoutID);
    taskTimeoutID = -1;
  };

代碼里的注釋寫得很清楚了把,但有幾個點可以說一下。

1.首先選擇宏任務(wù),因為我們需要去及時的讓出主線程(微任務(wù)并不會讓出主線程也是在更新頁面前去執(zhí)行)。

2.其次是宏任務(wù)中的選擇,MessageChannel,setTimeout,requestAnimationFrame,都是宏任務(wù),setTimeout會浪費4ms(這個大伙可以去看看),requestAnimationFrame的觸發(fā)時間是不穩(wěn)定的(可以看看瀏覽器的更新頁面機(jī)制),所以我猜想最后就選了MessageChannel把。

4.總結(jié)

其實到這思路也比較明了了,把React中為什么不使用requestIdleCallback理清楚,還順便把React的核心調(diào)度原理看了一下。

5.吐槽

唉,其實看源碼和手寫源碼完全是兩種感覺,更多的是體現(xiàn)在實現(xiàn)細(xì)節(jié)和代碼耦合性健壯性的問題,寫是怎么寫都行,但如何寫優(yōu)雅的方便人迭代的代碼就好燒腦。比如你就想實現(xiàn)一個fiber的大體思路就不難,但是如果想你在fiber上加hook,難度幾何飆升,基礎(chǔ)構(gòu)建和細(xì)節(jié)實現(xiàn)就很重要了,手寫肯定是不等于抄,還需要在里面加寫自己的想法和如何簡化的方案。

原文鏈接:https://juejin.cn/post/7170095557443551246

欄目分類
最近更新