这是我参与「第五届青训营 」伴学笔记创作活动的第 8 天
与其他框架相比,React 的 diff 算法有何不同?
概念
真实Dom首先会映射为虚拟Dom
当虚拟Dom变化后会根据差距生成patch,patch是结构化的数据,包含增加,更新,移除等
根据patch去更新真实Dom,反馈到用户界面上
更新时机
state变化
hooks调用之后
优化策略
- Fiber机制下节点和树分别采用FiberNode和FiberTree进行重构
- Fiber机制下整个更新过程由current与workInProgress两株树双缓冲完成
其他框架
Preact
没有patch的过程,直接进行属性更新
Vue2.0
Vue2.0使用了
snabbdom,整体思路和React相同,但在元素对比上,如果新旧两元素是同一元素,且没有设置key时,snabbdom在Diff子元素中会一次性对比旧节点,新节点及他们首尾四个节点,以及验证列表是否有变化
如何优化代码
避免跨层级节点移动
设置唯一key
尽量减少组件层级深度,因为过深的层级会加深遍历深度,带来性能问题
设置shouldComponentUpdate或者React.pureComponent减少Diff次数
VirtualDom的工作原理是什么?
什么是虚拟Dom
虚拟Dom是对真实Dom的一层抽象,以
javascriptVnode节点作为基础的树,用于描述真实节点,最终可以通过一系列手段映射给真实Dom,
为什么我们需要虚拟Dom
操作Dom的代价是昂贵的,哪怕是一个div都包含了巨量的属性,频繁操作会导致页面卡顿
React如何将虚拟Dom转变真实Dom
graph LR
Jsx --babel--> React.createElement --收集,处理属性,返回newElement--> ReactDom.render -- 对text,普通节点,函数,类组件分别进行不同的更新挂载-->挂载在父节点下
虚拟Dom的优势在哪里
降低了代码风险
跨平台的成本低 ,通过对虚拟Dom的结构描述对象实现多端转换
缺点
- 内存占用高
- 无法进行机制优化
其他应用场景:用于埋点统计和数据记录
解释React渲染流程
概念
React的渲染过程大致一致,但具体协调不同,以react16为分界线分为StackReconciler和FiberReconsiler,这里的协调从狭义上来讲特制react的diff算法,广义上来讲是react的reconsiler模块,通常包含了diff算法和一些公共逻辑
stackreconciler
它的核心调度算法是递归,调度的基本单位是事务,这里的事务是react团队从后端提取到的概念,
是通过ReactMount模块完成,更新通过ReactUpdate完成,模块之间相互分离,落脚的执行点也是事务,
fiberreconciler
特点
协作式多任务模式:在这个模式下,线程会定时放弃自己的运行权利交还给主线程,
策略优先级模式:通过标记tag的方式分优先级执行,比如动画等标记为high的任务会优先执行,
概念
基本单位: fiber
fiber:通过reactElement进行二次封装,提供了指向父子兄弟节点的引用,给diff的双向链表提供了实现基础
生命周期
render阶段
特点:可中断,可停止,无副作用
构建:通过构建workInProgress树计算出diff,以current树为基础,每个fiber为基础单位,自下而上的逐个检查和构造workInprogress树,这个过程是基于递归而不是循环来完成
执行:通过request和callback来执行每组任务,每组的每个任务被称为work,每个work完成后会检查是否有优先级更高的work需要插入,如果有就插入,如果没有就继续执行**,优先级通常是动画或者标记为high的会先进行处理**,每完成一组后会把调度权交还给主线程,直到下次request和callback调用,继续构建workInprogress树,
commit阶段
特点:不可暂停
处理Effects列表:根据Effects列表更新Dom树,回调生命周期,响应reference等,但要注意这个阶段是同步执行的不可暂停,所以不要在componentWillUnmount,componentDidmount,componentDidUpdate中执行重度消耗算力的任务
总结
日常任务重两种渲染流程差别不大,但在动画之类占用主线程的情况中,stackReconsiler会卡顿,而fiberReconsiler会带来高性能的表现
拓展
为什么Stack Reconsiler 模式下 render函数不支持return数组呢
递归只能返回一个节点,不支持数组