react原理

基本概念

  • 屏幕刷新率
    目前大多数设备每一秒刷新60次,每一次称作1帧,每一帧的执行时间大约是16ms,所以当每一帧的执行时间不大于16ms,才能保证不出现卡顿情况
  • 帧生命周期 life of frame
    • 处理用户事件(输入、点击、滚轮、滑动等)
    • 执行js代码
    • 开始帧
    • 执行requestAnimationFrame回调
    • 布局
    • 绘制
    • 帧空闲时间(requestIdleCallback)
      16ms
  • requestAnimationFrame
    <div id="div" style="width:0;background:red;height:20px;margin-bottom: 10px;"></div>
    <button id="btn">开始</button>
    
    <script>
      // 通过requestAnimationFrame绘制进度条
      const div = document.getElementById('div');
      let lastTime;
      function progress() {
        const current = Date.now();
        console.log('时间差:', current - lastTime);
        lastTime = current;
        const width = parseInt(div.style.width);
        if(width<100) {
          div.style.width = width + 1 + '%';
          div.innerText = div.style.width;
          requestAnimationFrame(progress);
        }
      }
      document.getElementById('btn').addEventListener('click', () => {
        lastTime = Date.now();
        div.style.width = 0;
        progress();
      })
    </script>
    
    示例
  • 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就会检查当前帧还有多少剩余时间,及时释放资源


    image.png
  • 是一种链表形式的数据结构,virtualDom的每一个节点就是一个fiber

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

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 });
      
    • 提交阶段:对协调阶段的产物(需要变更的节点)进行提交执行,不可以被中断,必须同步一次性

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,242评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,769评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,484评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,133评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,007评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,080评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,496评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,190评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,464评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,549评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,330评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,205评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,567评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,889评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,160评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,475评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,650评论 2 335

推荐阅读更多精彩内容