目录
引言
开始正文之前,必须要提一下虚拟DOM (计算机上都是虚拟的你把握不住的(bushi) ,hhhh~)
Virtual Dom 本质上是一个JavaScript对象,通过对象的方式来表示DOM结构;是对真实 DOM 的抽象,更加轻量级。将页面的状态抽象为JS对象的形式,配合不同的渲染工具,使跨平台渲染成为可能。通过事务处理机制,将多次DOM修改的结果一次性的更新到页面上,在每次数据发生变化前,虚拟dom都会缓存一份,变化时现在的虚拟dom会与缓存的虚拟dom进行比较,尽量避免手动操作DOM,从而有效的减少页面渲染的次数,减少修改DOM的重绘重排次数,提高渲染性能。
diff 算法原理
diff 算法的基本流程
- 真实的 DOM 首先会映射为虚拟 DOM;
- 当虚拟 DOM 发生变化后,就会根据差距计算生成 patch,这个 patch 是一个结构化的数据,内容包含了增加、更新、移除等;
- 根据 patch 去针对性的更新真实 DOM,反馈到用户的界面上。
react diff 算法的对比策略:
1. 基于树进行对比(忽略节点跨层级操作场景)
两棵树只对同一层次的节点进行比较,如果发现节点已经不存在了,则该节点及其子节点会被完全删除掉,不会用于进一步的比较,这就提升了比对效率。
2. 基于组件进行对比
同类型的两个组件,直接比较Virtual DOM树;不同类型的组件将会直接删除或创建新组件就导致重新渲染。
3. 基于节点进行对比
对于同一层级的一组子节点,diff 提供了3种节点重排操作:插入、移动、删除,效率消耗较大。通过标记 key (作用如下)的方式,React 可以直接移动 DOM 节点,降低内耗。
key 的作用
Key 是 React (Vue) 自己用于追踪哪些列表中元素被修改、被添加或者被移除的辅助标识(不会出现在标签里)。diff 算法中 React (Vue) 会借助元素的 Key 值来判断元素是否需重新渲染,并判断元素与本地状态的关联关系。
实例演示
index作为key有时会出问题:
推荐用唯一标识(id)作为key
react 和 vue diff 算法的区别
a. 相同点: 都是同层 differ,复杂度都为 O(n);
b. 不同点:
1.React 首位是除删除外是固定不动的,然后依次遍历对比;
2.Vue 的compile 阶段的optimize标记了static 点,可以减少 differ 次数,而且是采用双向遍历方法;
c. 现代前端框架有两种方式侦测变化,一种是 pull ,一种是 push
pull: 其代表为React,React通常会用setStateAPI显式更新,然后React会进行一层层的Virtual Dom Diff操作找出差异,然后Patch到DOM上,React从一开始就不知道到底是哪发生了变化,只是知道「有变化了」,然后再进行比较暴力的Diff操作查找「哪发生变化了」,另外一个代表就是Angular的脏检查操作。
push: Vue的响应式系统则是push的代表,当Vue程序初始化的时候就会对数据data进行依赖的收集,一但数据发生变化,响应式系统就会立刻得知。因此Vue是一开始就知道是「在哪发生变化了」,但是这又会产生一个问题,通常一个绑定一个数据就需要一个Watcher;一但我们的绑定细粒度过高就会产生大量的Watcher,这会带来内存以及依赖追踪的开销,而细粒度过低会无法精准侦测变化,因此Vue的设计是选择中等细粒度的方案,在组件级别进行push侦测的方式,通常会第一时间侦测到发生变化的组件,然后在组件内部进行Virtual Dom Diff获取更加具体的差异,而Virtual Dom Diff则是pull操作,Vue是push+pull结合的方式进行变化侦测的。