传统的diff算法
juejin.im/post/5c8e5e… 比较完整的diff过程 经典***
理解:
给定任意两棵树,采用先序深度优先遍历的算法找到最少的转换步骤。diff比较两个虚拟DOM的区别,也就是在比较两个对象的区别。
作用: 根据两个虚拟对象创建出补丁,描述改变的内容,将这个补丁用来更新DOM
我们来梳理一下整个DOM-diff的过程:
- 用JS对象模拟DOM(虚拟DOM)
- 把此虚拟DOM转成真实DOM并插入页面中(render)
- 如果有事件发生修改了虚拟DOM,比较两棵虚拟DOM树的差异,得到差异对象(diff)
- 把差异对象应用到真正的DOM树上(patch)
为什么虚拟DOM可以提高性能:
虚拟 dom 相当于在 js 和真实 dom 中间加了一个缓存,利用 dom diff 算法避免了没有必要的 dom 操作,从而提高性能。
用 JavaScript 对象结构表示 DOM 树的结构;然后用这个树构建一个真正的 DOM 树,插到文档当中当状态变更的时候,重新构造一棵新的对象树。然后用新的树和旧的树进行比较,记录两棵树差异把 2 所记录的差异应用到步骤 1 所构建的真正的 DOM 树上,视图就更新了。
基于中Diff的开发建议
基于tree diff:
- 开发组件时,注意保持DOM结构的稳定;即,尽可能少地动态操作DOM结构,尤其是移动操作。
- 当节点数过大或者页面更新次数过多时,页面卡顿的现象会比较明显。
- 这时可以通过 CSS 隐藏或显示节点,而不是真的移除或添加 DOM 节点。
基于component diff:
- 注意使用 shouldComponentUpdate() 来减少组件不必要的更新。
- 对于类似的结构应该尽量封装成组件,既减少代码量,又能减少component diff的性能消耗。
基于element diff:
- 对于列表结构,尽量减少类似将最后一个节点移动到列表首部的操作,当节点数量过大或更新操作过于频繁时,在一定程度上会影响 React 的渲染性能。
- 更准确
因为带key就不是就地复用了,在sameNode函数 a.key === b.key对比中可以避免就地复用的情况。所以会更加准确。
- 更快
利用key的唯一性生成map对象来获取对应节点,比遍历方式更快。(这个观点,就是我最初的那个观点。从这个角度看,map会比遍历更快。)
React 和 Vue 的 diff 时间复杂度从 O(n^3) 优化到 O(n) ,那么 O(n^3) 和 O(n) 是如何计算出来的?
我认为react做了三种优化来降低复杂度:
1:如果父节点不同,放弃对子节点的比较,直接删除旧节点然后添加新的节点重新渲染;
2:如果子节点有变化,Virtual DOM不会计算变化的是什么,而是重新渲染,
3:通过唯一的key策略