Vue2.x diff算法
Vue2.x diff算法原理
传统diff算法通过循环递归对节点进行依次对比效率低下,算法复杂度达到O(N3),主要原因在于其追求完全比对和最小修改,而React、Vue则是放弃了完全比对及最小修改,才实现从O(N3) => O(N)。
优化措施有:
- 「分层diff」:不考虑跨层级移动节点,让新旧两个VDOM树的比对无需循环递归(复杂度大幅优化,直接下降一个数量级的首要条件)。这个前提也是Web UI 中 DOM 节点跨层级的移动操作特别少,可以忽略不计。
在同层节点中,采用了**「双端比较的算法」**过程可以概括为:oldCh和newCh各有两个头尾的变量StartIdx和EndIdx,它们的2个变量相互比较,一共有4种比较方式。如果4种比较都没匹配,如果设置了key,就会用key进行比较,在比较的过程中,变量会往中间靠,一旦StartIdx>EndIdx表明oldCh和newCh至少有一个已经遍历完了,就会结束比较;
当发生以下情况则跳过比对,变为插入或删除操作:
-
「组件的Type(Tagname)不一致」,原因是绝大多数情况拥有相同type的两个组件将会生成相似的树形结构,拥有不同type的两个组件将会生成不同的树形结构,所以type不一致可以放弃继续比对。
-
「列表组件的Key不一致」,旧树中无新Key或反之。毕竟key是元素的身份id,能直接对应上是否是同一个节点。
-
对触发了getter/setter 的组件进行diff,精准减少diff范围
Vue3.0 diff
diff痛点
vue2.x中的虚拟dom是进行**「全量的对比」,在运行时会对所有节点生成一个虚拟节点树,当页面数据发生变更好,会遍历判断virtual dom所有节点(包括一些不会变化的节点)有没有发生变化;虽然说diff算法确实减少了多DOM节点的直接操作,但是这个「减少是有成本的」,如果是复杂的大型项目,必然存在很复杂的父子关系的VNode,「而Vue2.x的diff算法,会不断地递归调用 patchVNode,不断堆叠而成的几毫秒,最终就会造成 VNode 更新缓慢」**。
那么Vue3.0是如何解决这些问题的呢
动静结合 PatchFlag
来个???:
在Vue3.0中,在这个模版编译时,编译器会在动态标签末尾加上 /* Text*/ PatchFlag。**「也就是在生成VNode的时候,同时打上标记,在这个基础上再进行核心的diff算法」**并且 PatchFlag 会标识动态的属性类型有哪些,比如这里 的TEXT 表示只有节点中的文字是动态的。而patchFlag的类型也很多。这里直接引用一张图片。
其中大致可以分为两类:
-
当 patchFlag 的值「大于」 0 时,代表所对应的元素在 patchVNode 时或 render 时是可以被优化生成或更新的。
-
当 patchFlag 的值「小于」 0 时,代表所对应的元素在 patchVNode 时&#