美文网首页
React深入6-源码2(React scheduler)

React深入6-源码2(React scheduler)

作者: 申_9a33 | 来源:发表于2022-03-06 18:22 被阅读0次

    写在前面

    • react fiber通过链表,将任务碎片化了,在上一篇博客React fiber中,使用window.requestIdleCallback 让浏览器在空闲时间执行碎片化的计算渲染任务,但是 IE和Safari浏览器,目前并不支持该Api.所以需要我们自己实现scheduler,来执行已经碎片化的任务
    • 大致思路就是,给每个任务根据优先级不同,在每次Event Loop中设置可用时间,时间到了,任务停止。先让浏览器渲染,没执行完的任务,下一次EventLoop接着执行,保证用户交互的流畅性
    • 先看 scheduler 流程图


      scheduler.png
    • 本次代码在源码1的基础上完成

    创建 src\react\scheduler.ts

    /* eslint-disable @typescript-eslint/no-use-before-define */
    
    const taskQueue:Array<{ callback:Function }> = [];
    const timerQueue:Function[] = [];
    
    let deadline = 0;
    const threshold = 5;
    
    export function schedulerCallback(callback:Function) {
      const newTask = { callback };
      taskQueue.push(newTask);
      schedule(flushWork);
    }
    
    export function schedule(callback:Function) {
      timerQueue.push(callback);
      postMessage();
    }
    
    function postMessage() {
      const { port1, port2 } = new MessageChannel();
    
      port1.onmessage = () => {
        const tem = timerQueue.splice(0, timerQueue.length);
        tem.forEach((fn) => fn());
      };
    
      port2.postMessage(undefined);
    }
    
    function flushWork() {
      // 记录当前时间
      deadline = getCurrentTime() + threshold;
    
      const currentTask = taskQueue[0];
      if (currentTask) {
        const { callback } = currentTask;
        let flag = true;
        while (flag) {
          if (callback()) {
            // taskLoop 执行完毕,退出循环
            flag = false;
          } else if (shouldYield()) {
            // 给定运行时间消耗完毕,退出循环
            flag = false;
    
            // 重新添加到调度中,下次执行
            schedulerCallback(callback);
          }
        }
    
        taskQueue.shift();
      }
    }
    
    export function shouldYield() {
      return getCurrentTime() >= deadline;
    }
    
    export function getCurrentTime() {
      return performance.now();
    }
    
    • 代码比较少,逻辑就是上面画的流程图
    • MessageChannel 类似与 setTimeout ,但是比setTimeout先执行

    修改 src\react\ReactFiberWorkLoop.ts

    export function scheduleUpdateOnFiber(fiber:any) {
      wipRoot = fiber;
      wipRoot.sibling = null;
      nextUnitOfwork = wipRoot;
      schedulerCallback(workLoop);
    }
    
    // 使用自己实现的调度,返回值:true 执行完成, false 还没有执行完成
    function workLoop() {
      if (nextUnitOfwork) {
        nextUnitOfwork = performUnitOfWork(nextUnitOfwork);
    
      } else {
        if (wipRoot) {
          commitRoot();
        }
    
        return true;
      }
    
      return false;
    }
    

    源码

    相关文章

      网友评论

          本文标题:React深入6-源码2(React scheduler)

          本文链接:https://www.haomeiwen.com/subject/txjwrrtx.html