React Fiber实现原理分析
Author:zhoulujun Date:
React 15 会出现掉帧的现象
旧版 React 通过递归的方式进行渲染,使用的是 JS 引擎自身的函数调用栈,它会一直执行到栈空为止。
在页面元素很多,且需要频繁刷新的场景下,React 15 会出现掉帧的现象
解决主线程长时间被 JS 运算占用这一问题的基本思路,是将运算切割为多个步骤,分批完成。也就是说在完成一部分任务之后,将控制权交回给浏览器,让浏览器有时间进行页面的渲染。等浏览器忙完之后,再继续之前未完成的任务。
Fiber实现了自己的组件调用栈,它以链表的形式遍历组件树,可以灵活的暂停、继续和丢弃执行的任务。实现方式是使用了浏览器的requestIdleCallback这一 API。官方的解释是这样的:
window.requestIdleCallback()会在浏览器空闲时期依次调用函数,这就可以让开发者在主事件循环中执行后台或低优先级的任务,而且不会对像动画和用户交互这些延迟触发但关键的事件产生影响。函数一般会按先进先调用的顺序执行,除非函数在浏览器调用它之前就到了它的超时时间。具体参看《requestIdleCallback对比setTimeout/setImmediate/requestAnimationFrame》
解决的层面在Reconciler
React 框架内部的运作可以分为 3 层:
Virtual DOM 层,描述页面长什么样。
Reconciler 层,负责调用组件生命周期方法,进行 Diff 运算等。
Renderer 层,根据不同的平台,渲染出相应的页面,比较常见的是 ReactDOM 和 ReactNative。
优化的层面在 Reconciler 层了,React 团队也给它起了个新的名字,叫Fiber Reconciler。这就引入另一个关键词:Fiber。
Fiber
Fiber 其实指的是一种数据结构,它可以用一个纯 JS 对象来表示:
const fiber = { stateNode, // 节点实例 child, // 子节点 sibling, // 兄弟节点 return, // 父节点 }
为了加以区分,以前的 Reconciler 被命名为Stack Reconciler。Stack Reconciler 运作的过程是不能被打断的,必须一条道走到黑
而 Fiber Reconciler 每执行一段时间,都会将控制权交回给浏览器,可以分段执行
为了达到这种效果,就需要有一个调度器 (Scheduler) 来进行任务分配。任务的优先级有六种:
synchronous,与之前的Stack Reconciler操作一样,同步执行
task,在next tick之前执行
animation,下一帧之前执行
high,在不久的将来立即执行
low,稍微延迟执行也没关系
offscreen,下一次render时或scroll时才执行
优先级高的任务(如键盘输入)可以打断优先级低的任务(如Diff)的执行,从而更快的生效。
Fiber Tree
Fiber Reconciler 在阶段一进行 Diff 计算的时候,会生成一棵 Fiber 树。这棵树是在 Virtual DOM 树的基础上增加额外的信息来生成的,它本质来说是一个链表。
Fiber 树在首次渲染的时候会一次过生成。在后续需要 Diff 的时候,会根据已有树和最新 Virtual DOM 的信息,生成一棵新的树。这颗新树每生成一个新的节点,都会将控制权交回给主线程,去检查有没有优先级更高的任务需要执行。如果没有,则继续构建树的过程:
如果过程中有优先级更高的任务需要进行,则 Fiber Reconciler 会丢弃正在生成的树,在空闲的时候再重新执行一遍。
在构造 Fiber 树的过程中,Fiber Reconciler 会将需要更新的节点信息保存在Effect List当中,在阶段二执行的时候,会批量更新相应的节点。
参考文章:
浅析React Diff 与 Fiber https://zhuanlan.zhihu.com/p/58863799
React.js 的 context huziketang.mangojuice.top/books/react/lesson29
React Fiber 原理介绍 https://segmentfault.com/a/1190000018250127
转载本站文章《React Fiber实现原理分析》,
请注明出处:https://www.zhoulujun.cn/html/webfront/ECMAScript/jsBase/2020_0628_8490.html
延伸阅读:
- grunt使用yeoman自动化构建react项目……
- browser.js什么鬼?作用是什么
- React on ES6+:react.component vs react.createclass的异同
- React项目中常见的技术坑与优化及Component Generator
- react更新组件componentWillReceiveProp里面setState无效,未触发渲染
- can't resolve 'redux-thunk' in *** 项目不能跑起
- React+redux组件最简单的计算器!
- react+redux渲染页面空白,原来是大小写惹的祸害
- react:Uncaught TypeError: Cannot read property
- react组件中bind(this)写在哪里好?
- react-dom.min.js:15 Uncaught (in promise) Error: Minified React error
- react hook context 管理全局状态
- TypeScript写React组件默认属性问题
- React Query与SWR尚能饭否?React Query还真香!
- React 同构实践与思考
- React代数效应学习笔记
- ReactHook详解:memo/useMemo/useCallback等钩子细讲
- jsx动态class写法:vue3与react+classname库
- react异步数据如ajax请求应该放在哪个生命周期?
- React 源码剖析系列—生命周期的管理艺术—有限状态机
- React 源码剖析系列 - 解密 setState
- React16源码分析(2):react.development.js源码注释
- React16源码分析(1):react项目架构/文件目录/包结构解读
- React16源码分析(0):对象池/合成事件/事务机制等概念科普
- 再谈react优势——react技术栈回顾