react原理

基本概念

  • 屏幕刷新率
    目前大多数设备每一秒刷新60次,每一次称作1帧,每一帧的执行时间大约是16ms,所以当每一帧的执行时间不大于16ms,才能保证不出现卡顿情况
  • 帧生命周期 life of frame
    • 处理用户事件(输入、点击、滚轮、滑动等)
    • 执行js代码
    • 开始帧
    • 执行requestAnimationFrame回调
    • 布局
    • 绘制
    • 帧空闲时间(requestIdleCallback)
      react原理_第1张图片
      16ms
  • requestAnimationFrame
    react原理_第2张图片
    示例
  • react中的基础数据结构:链表
    以下代码模拟了一个单链表数据结构的update操作,依次执行每一个update,最终得到最终的结果
    // 每一个更新单元,单元中包含的载荷数据、以及下一个更新单元的指针
    class Update {
      constructor(payload) {
        this.payload = payload;
        this.nextUpdate = null;
      }
    }
    // 更新队列,包含一个首节点、和一个尾结点
    class UpdateQueue {
      constructor() {
        this.firstUpdate = null;
        this.lastUpdate = null;
      }
      // 追加更新单元到队列
      enqueueUpdate(update) {
        if (!this.firstUpdate) {
          this.firstUpdate = this.lastUpdate = update;
        } else {
          this.lastUpdate.nextUpdate = update;
          this.lastUpdate = update;
        }
      }
      // 执行队列
      forceUpdate() {
        let curUpdate = this.firstUpdate;
        let state = {};
        while (curUpdate) {
          const curState = typeof curUpdate.payload === 'function' ? curUpdate.payload(state) : curUpdate.payload;
          state = { ...state, ...curState };
          curUpdate = curUpdate.nextUpdate;
        }
        return state;
      }
    }
    
    const queue = new UpdateQueue();
    let update = new Update({ name: 'kerry' });
    queue.enqueueUpdate(update);
    update = new Update({ age: 20 });
    queue.enqueueUpdate(update);
    update = new Update(state => ({ age: state.age + 1 }));
    queue.enqueueUpdate(update);
    update = new Update(state => ({ name: state.name + 'is good' }));
    queue.enqueueUpdate(update);
    // 最终结果: { name: 'kerryis good', age: 21 }
    console.log(queue.forceUpdate());
    

fiber之前的react

babel在编译jsx代码的时候,会将其转换成React.createElement方法,运行时得到一个虚拟dom树
react会递归比对虚拟dom树,找出需要变动的节点,同步更新它们。这个过程称之为Reconcilation(协调)
在协调期间,React会一直占用浏览器资源,不能够中断,当一个项目足够大的时候,会有很深的调用栈,而且长时间无法释放浏览器资源,造成卡顿

 const root = {
   key: 'a1',
   children: [
     {
       key: 'b1',
       children: [
         {
           key: 'c1',
           children: [],
         }, 
         {
           key: 'c2',
           children: [],
         }
       ]
     },
     {
       key: 'b2',
       children: []
     }
   ]
 }

 walk(root);

 function walk(vDom) {
   doWork(vDom);
   vDom.children.forEach(child => {
     walk(child);
   })
 }

 function doWork(vDom) {
   console.log(vDom.key);
 }

fiber是什么

  • 是一种调度策略,合理的去分配资源。让Reconcilation变得更协调,让React的协调过程变得可以随时中断,适时的让出主线程去响应高优先级的用户操作。
  • 是一个执行单元,每次执行完一个执行单元,react就会检查当前帧还有多少剩余时间,及时释放资源


    react原理_第3张图片
    image.png
  • 是一种链表形式的数据结构,virtualDom的每一个节点就是一个fiber

    fiber的重要属性
    type:类型
    childe:第一个子节点
    sibling:下一个弟弟节点
    return:父节点

react原理_第4张图片
fiber链表结构描述了上述root结构的一个虚拟dom树

fiber执行阶段

  • 每一次的渲染有两个阶段:协调阶段(render执行得到一个fiber树和Reconciliation)和提交阶段(commit)
    • 协调阶段:实质上就是一个diff操作,这个阶段会遍历找出所有需要变更(增删改)的节点,可以被中断,这些变更被称之为react的副作用(effect)

      遍历规则:深度优先遍历。从根节点出发,对于每一个节点来说优先遍历child,没有child找sibling,没有sibling,没有sibling找return的sibling。(遵循皇位继承原则,长子优先->次子->弟弟)。
      所以上述root节点的遍历顺序为:A1->B1->C1->C2->B2

      let nextUnitWork = null;
      function workLoop(deadline) {
       // 如果存在待执行单元,切当前帧剩余时间大于5ms或超时,则执行待执行单元 
       while ((deadline.timeRemaining() > 5 || deadline.didTimeout) && nextUnitWork) {
         nextUnitWork = performUnitWork(nextUnitWork);
       }
       if (!nextUnitWork) {
         console.log('render阶段结束');
       } else {
         requestIdleCallback(workLoop, { timeout: 1000 });
       }
      }
      function performUnitWork(fiber) {
       beginWork(fiber);
       if (fiber.child)
         return fiber.child; 
       // 没有child 当前fiber执行完成,并找到父fiber的兄弟节点
       while (fiber) {
         completeWork(fiber);
         if (fiber.sibling)
           return fiber.sibling;
         fiber = fiber.return;
       }
      }
      function beginWork(fiber) {
       console.log('当前执行:' + fiber.key);
      }
      function completeWork(fiber) {
       console.log('执行完成:' + fiber.key);
      }
      nextUnitWork = root;
      // 调用requestIdleCallback利用帧剩余时间去做协调diff
      requestIdleCallback(workLoop, { timeout: 1000 });
      
    • 提交阶段:对协调阶段的产物(需要变更的节点)进行提交执行,不可以被中断,必须同步一次性

你可能感兴趣的:(react原理)