vdom解决三个问题
- 高效的diff算法
- 只要更新需要更新的dom节点
- 数据变化监测, batch dom读写操作
diff算法
- 递归: O(n^3)
- react vdom diff O(n)
diff策略
- 很少跨层,所以我们就同层比较,不跨层比较
- 相同的Component又相同的tree结构,不同的Component有不同的tree结构
- 同一层级的子节点,key
对应tree diff、component diff、element diff
tree diff
updateDepth: 对dom树的层级进行控制
Q: 如果有跨层级的移动,应该如何处理?
先对应的创建,再删除
component diff
- same component
- different component, dirty component, 替换所有的子节点
- shouldComponentUpdate()
element diff
- insert_markup, move_existing, delete_node
move_existing
generateComponentChildren 调用 receiveComponent, prev = next
from: A, B, C, D
to: B, A, D, C
key
for..in遍历新的拿到key, 去旧的节点去找如果存在就移动,移动之前会
lastindex, 顺序优化:如果新的节点的位置大于旧的lastindex说明是新增
总结
- O(n^3)=> O(n)
- 避免将最后一个元素移动到最前面
- 分层比较 tree diff
- Component diff tree diff
- key => element diff
- 建议:开发组件,dom结构稳定,提升性能
- 建议:避免直接将行尾元素插到最前面
- shouldCompoentUpdate 需不需要做diff
snabbdom github库 (vue2.0借鉴)
双端比较算法
inferno.js 号称最快的diff算法 (vue3.0借鉴)
abcd dabc 只移动d,然而react却需要移动abc,这也是react设计不好的地方
调度
fiber
- vdom层
- reconciler层 做一些dom diff 生命周期钩子一些复杂的运算
- render层 reactDOM、rn
作用就是帮你做一些切片
在setState的时候,开始协调,遍历你的vdom,然后去diff,然后拿到真实的dom,然后紧接着传给render去渲染
time slice
requestIdleCallback && requestAnimationFrame
- requestIdleCallback: 浏览器空闲的时候去调用,回调不一定去执行
正常浏览器一帧都进行那些操作:
- 交互操作(touch,wheel blocking input event === click、keypress Non Blocking input event)
- Timers Js执行
- begin frame (window resize、scroll、mediaquery change、animation events)
- rAF (requestAnimationFrame Callbacks、Intersection Observer Callbacks)调用
- Layout(1. Recalc style 2. Update layout 3. Resize Observer Callbacks)
- Paint(1. Compositing Update 2. Paint Invilidation 3. Record)
idleCallBack执行位置
input => rAF => frame commit => idle period(idleCallback,idleCallback) => input...循环
react fiber reconciler(调度)
- 计算任务的time(expirationTime)
- requestIdleCallback polyfill版本
MessageChannel
渲染以后宏任务先执行
requestAnimationFrame解决多次(后台不执行可以使用settimeout替换)+ 计算frame时间和下个frame时间 + messageChannel
expirationTime
当前时间(performance.now()) + 常量(任务优先级)