react fiber

2020-08-30  本文已影响0人  一土二月鸟

一帧的生命周期

requestIdleCallback

    function sleep(time) {
      let startTime = Date.now();
      while (Date.now() - startTime < time) {
        continue;
      }
    }

    const fnArr = [
      function () {
        sleep(20);
        console.log('任务1')
      },
      function () {
        sleep(20);
        console.log('任务2')
      },
      function () {
        console.log('任务3')
      }
    ];


    function rIdCb (deadline) {
      console.log('此帧剩余时间' + deadline.timeRemaining());
      // timeRemaining 代表剩余时间; didTimeout代表是否已超时;
      while ((deadline.timeRemaining() > 0 || deadline.didTimeout) && fnArr.length > 0) {
        fnArr.shift()();
      }
      if(fnArr.length > 0){
       requestIdleCallback(rIdCb, {timeout: 1000}); // timeout代表执行rIdCb的超时时间,如果超过该时间仍未执行,didTimeout则为true;
      }
    }

    requestIdleCallback(rIdCb, { timeout: 1000 });

requestAnimationFrame

<!DOCTYPE html>
<html lang="zh-CN">

<head>
  <meta charset="UTF-8" />
  <title>test</title>
  <style>
    .box {
      width: 0px;
      height: 20px;
      background: lightgray;
    }
  </style>
</head>

<body>
  <div class="box"></div>
  <button id="btn">change width</button>
  <script type="text/javascript">

    const obtn = document.getElementById('btn');
    const odiv = document.getElementsByClassName('box')[0];
    let start = 0;
    obtn.addEventListener('click', () => {
      odiv.style.width = 0;
      start = Date.now();
      requestAnimationFrame(rafCb);
    })

    const rafCb = () => {
      let currTime = Date.now();
      console.log(currTime - start);  // 此时间差为每一帧的执行周期,为16ms左右
      start = currTime;
      odiv.style.width = odiv.offsetWidth + 1 + 'px'; 
      odiv.innerHTML = odiv.offsetWidth + '%';

      if (odiv.offsetWidth < 100) {
        requestAnimationFrame(rafCb); // 利用raf代替常规的while循环或递归,可实现16ms执行一次rafCb,实现了既不占用主线程,动画又平滑的效果
      }
    }

  </script>
</body>

</html>

单链表

// 定义第一份数据
let data1 = { x: 1 };
// 将第一份数据data1的内存地址指向火车头和最后一节车厢
let huochetou = lastone = data1;
// 定义第二份数据
let data2 = { y: 2 };
// 将第二份数据data2的内存地址指向最后一节车厢的next属性。(与此同时,火车头也拥有的next属性,其值为data2);
lastone.next = data2;
// 将第二份数据data2的内存地址指向最后一节车厢lastone。(此时,火车头的next属性和lastone指向的都是data2的内存地址);
lastone = data2;
// 定义第三份数据
let data3 = { z: 3 };
// 将第三份数据data3的内存地址指向lastone的next属性。(此时火车头的next将新增next属性,并指向data3)
lastone.next = data3;
// 将lastone指向data3
lastone = data3;

最终在火车头上通过next实现了单链表效果
<!DOCTYPE html>
<html lang="zh-CN">

<head>
  <meta charset="UTF-8" />
  <title>test</title>
</head>

<body>
  <div class="box"></div>
  <button id="btn">change width</button>
  <script type="text/javascript">

    class Update {
      constructor(payload, nextUpdate) {
        this.payload = payload;
        this.nextUpdate = nextUpdate;
      }
    }

    class UpdateQueue {
      constructor() {
        this.baseState = null;
        this.firstUpdate = null;
        this.lastUpdate = null;
      }
      enqueueUpdate(update) {
        if (this.firstUpdate === null) {
          this.lastUpdate = update;
          this.firstUpdate = this.lastUpdate;
        } else {
          this.lastUpdate.nextUpdate = update;
          this.lastUpdate = update;
        }
      }

      forceUpdate() {
        let currentState = this.baseState || {}; // 初始状态
        let currentUpdate = this.firstUpdate;
        while (currentUpdate) {
          let nextState = typeof currentUpdate.payload === 'function'
            ? currentUpdate.payload(currentState)
            : currentUpdate.payload;
          currentState = { ...currentState, ...nextState };
          currentUpdate = currentUpdate.nextUpdate;
        }
        this.firstUpdate = this.lastUpdate = null;
        this.baseState = currentState;
        return currentState;
      }
    }

    let queue = new UpdateQueue();
    queue.enqueueUpdate(new Update({ name: 'skyler' }));
    queue.enqueueUpdate(new Update({ number: 0 }));
    queue.enqueueUpdate(new Update(state => ({ number: state.number + 1 })));
    queue.enqueueUpdate(new Update(state => ({ number: state.number + 1 })));
    console.log(queue.firstUpdate, queue.lastUpdate);
    queue.forceUpdate();
    console.log(queue.baseState);
  </script>
</body>

</html>

react的链表结构及遍历规则

const A1 = { type: 'div', key: 'A1' };
const B1 = { type: 'div', key: 'B1', return: A1 };
const B2 = { type: 'div', key: 'B2', return: A1 };
const C1 = { type: 'div', key: 'C1', return: B1 };
const C2 = { type: 'div', key: 'C2', return: B1 };

A1.child = B1;
B1.child = C1;
B1.sibling = B2;
C1.sibling = C2;
//  fiber 数据结构
const A1 = { type: 'div', key: 'A1' };
const B1 = { type: 'div', key: 'B1', return: A1 };
const B2 = { type: 'div', key: 'B2', return: A1 };
const C1 = { type: 'div', key: 'C1', return: B1 };
const C2 = { type: 'div', key: 'C2', return: B1 };

// 每个节点只有自己的父亲、大儿子、弟弟
A1.child = B1; // 每个父亲只携带一个大儿子,让大儿子携带大儿子自己的弟弟,以此建立链条关系
B1.child = C1;
B1.sibling = B2;
C1.sibling = C2;


/**
 * 1. 开始从顶点遍历。
 * 2. 如果有大儿子,先遍历大儿子。(同时要等自己所有的儿子任务完成自己的任务才算完成。)
 * 3. 如果没有大儿子,说明自己的任务已完成。开始找自己的弟弟。
 * 4. 如果找到弟弟,开始遍历弟弟。
 * 5. 没有弟弟,则通过自己的父亲找父亲的弟弟,同时代表自己的父亲已完成全部工作
 * 6. 父亲的弟弟开始循环上面的步骤,直到根节点完成任务
 */

let rootFiber = A1;  // A1为根节点 通知携带了自己的所有的children

// 1. 开始从顶点遍历
workLoop(rootFilber); 

// nextUnitOfWork为下一个执行单元(链表的单个节点,也叫做一个fiber)
function workLoop(nextUnitOfWork) {
  while (nextUnitOfWork) { // 如果有执行单元就执行,然后返回下一个执行执行单元
    nextUnitOfWork = performUnitOfWork(nextUnitOfWork);
  }

  console.log('render阶段结束')
}

// 处理一个fiber
function performUnitOfWork(fiber) {
  beginWork(fiber); // 此fiber开始工作
  if (fiber.child) { // 2. 如果有大儿子先遍历大儿子
    return fiber.child;
  } // 3. 如果没有大儿子,说明自己的任务已完成。开始找自己的弟弟。

  while (fiber) {
    completeUnitOfWork(fiber); 
    if (fiber.sibling) { // 4. 如果找到弟弟,开始遍历弟弟。
      return fiber.sibling;
    } // 5. 没有弟弟,则通过自己的父亲找父亲的弟弟,同时代表自己的父亲已完成全部工作
    fiber = fiber.return; 
  }

  return null; // 6. 代表任务执行到了rootFiber的父亲,它的父亲为null,所以本次任务结束运行

}

function completeUnitOfWork(fiber) {
  console.log(fiber.key, '执行完成');
}

function beginWork(fiber) {
  console.log(fiber.key, '开始执行'); // A1 B1 C1
}

requestIdleCallback和链表遍历相结合

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>

<body>
  <script>
    //  fiber 数据结构
    const A1 = { type: 'div', key: 'A1' };
    const B1 = { type: 'div', key: 'B1', return: A1 };
    const B2 = { type: 'div', key: 'B2', return: A1 };
    const C1 = { type: 'div', key: 'C1', return: B1 };
    const C2 = { type: 'div', key: 'C2', return: B1 };

    // 每个节点只有自己的父亲、大儿子、弟弟
    A1.child = B1; // 每个父亲只携带一个大儿子,让大儿子携带大儿子自己的弟弟,以此建立链条关系
    B1.child = C1;
    B1.sibling = B2;
    C1.sibling = C2;


    /**
     * 1. 开始从顶点遍历。
     * 2. 如果有大儿子,先遍历大儿子。(同时要等自己所有的儿子任务完成自己的任务才算完成。)
     * 3. 如果没有大儿子,说明自己的任务已完成。开始找自己的弟弟。
     * 4. 如果找到弟弟,开始遍历弟弟。
     * 5. 没有弟弟,则通过自己的父亲找父亲的弟弟,同时代表自己的父亲已完成全部工作
     * 6. 父亲的弟弟开始循环上面的步骤,直到根节点完成任务
     */

    let nextUnitOfWork = A1;

    let startTime = Date.now();

    // 1. 开始从顶点遍历
    requestIdleCallback(workLoop, { timeout: 1000 });

    // nextUnitOfWork为下一个执行单元(链表的单个节点,也叫做一个fiber)
    function workLoop(deadline) {
      while ((deadline.timeRemaining() > 0 || deadline.didTimeout) && nextUnitOfWork) { // 如果有执行单元并且本帧还有剩余时间就执行,然后返回下一个执行执行单元
        nextUnitOfWork = performUnitOfWork(nextUnitOfWork);
      }
      if (!nextUnitOfWork) {
        console.log('render阶段结束。共耗时:', Date.now() - startTime);
      } else {
        requestIdleCallback(workLoop, { timeout: 1000 })
      }
    }

    // 处理一个fiber
    function performUnitOfWork(fiber) {
      beginWork(fiber); // 此fiber开始工作
      if (fiber.child) { // 2. 如果有大儿子先遍历大儿子
        return fiber.child;
      } // 3. 如果没有大儿子,说明自己的任务已完成。开始找自己的弟弟。

      while (fiber) {
        completeUnitOfWork(fiber);
        if (fiber.sibling) { // 4. 如果找到弟弟,开始遍历弟弟。
          return fiber.sibling;
        } // 5. 没有弟弟,则通过自己的父亲找父亲的弟弟,同时代表自己的父亲已完成全部工作
        fiber = fiber.return;
      }

      return null; // 6. 代表任务执行到了rootFiber的父亲,它的父亲为null,所以本次任务结束运行

    }

    function completeUnitOfWork(fiber) {
      sleep(20);
      console.log(fiber.key, '执行完成');
    }

    function beginWork(fiber) {
      console.log(fiber.key, '开始执行'); // A1 B1 C1
    }

    function sleep(time) {
      let start = Date.now();
      while (Date.now() - start < time) { }
    }
  </script>
</body>

</html>
上一篇下一篇

猜你喜欢

热点阅读