一、Vdom

1.原生dom操作特点

(1)复杂的dom操作费时消耗性能

(2)以前用jquery,可以自行控制操作时机

2.数据驱动下dom操作方案 - Vdom

(1)虚拟dom核心思想

将更多的计算转移到js上,用js模拟dom,计算出最小更新,再进行原生dom操作

(2)用js模拟dom结构

一般会用tag描述标签名;props描述属性;children描述子元素

image.png

(3)snabbdom — 强大简洁的vdom工具库

vue是参考snabbdom实现的vdom和diff,vue3重写了vdom但是基本理念不变

二、Diff算法—Vdom的核心

diff算法能在日常使用中体现(如给元素添加key),也是前端面试热门宠儿

1、diff概念

diff是一个常见的概念,git 有diff命令,js中两个对象可以做diff(jiff工具),两颗树形结构也可以做diff,比如vodm 的diff

2.优化后的diff策略,时间复杂度降低到了O(n)

(1)只比较同一层级,不跨级比较

image.png

(2)tag不相同,则直接删掉重建,不再深度比较

当tag不相同时,猜测不一样,即使子元素相同。满足百分之80的情况
image.png

(3)当tag和key相同时,认为节点一样,不再深度比较

三、snabbdom使用

  1. const h = snabbdom.h
  2. const container = document.getElementById('container')
  3. // 生成 vnode
  4. const vnode = h('ul#list', {}, [
  5. h('li.item', {}, 'Item 1'),
  6. h('li.item', {}, 'Item 2')
  7. ])
  8. patch(container, vnode)
  9. document.getElementById('btn-change').addEventListener('click', () => {
  10. // 生成 newVnode
  11. const newVnode = h('ul#list', {}, [
  12. h('li.item', {}, 'Item 1'),
  13. h('li.item', {}, 'Item B'),
  14. h('li.item', {}, 'Item 3')
  15. ])
  16. patch(vnode, newVnode)
  17. // vnode = newVnode // patch 之后,应该用新的覆盖现有的 vnode ,否则每次 change 都是新旧对比
  18. })

1.h函数

接受参数,生成vnode

第一个参数sel: 标签名+选择器

第二个参数data:标签信息,可包含标签属性和事件绑定等

第三个参数children:子元素

2.patch方法

对比新旧vnode差异,再更新到真实dom

第一个参数:真实dom节点或者就vnode对象

第二个参数:新vnode或者null

3.patch过程

(1)调用sameVnode对比新旧VNode是否相同节点(节点的key和sel相同)

(2)如果不是相同节点,删除之前的内容,重新渲染

(3)如果是相同节点,再判断新的VNode是否有text,如果有并且和oldVnode的text不同直接更新文本内容(patchVnode)

(4)patcheVnode对比新旧VNode差异,如果新的VNode有children,调用updateChildren判断子节点是否有变化

(5)如果不满足上面其中特殊情况,遍历新节点children在旧节点children中查找是否有相同key的节点

a.如果存在相同key值节点,判断sel是否相同,如果相同,则调用patchVnode方法进行对比更新并修改对应index

b.如果不存在相同节点,则判断为新增节点,并渲染

vue中虚拟dom和diff - 图4
vue中虚拟dom和diff - 图5

四、Vdom和diff算法总结

image.png

五、vue中patch过程

1.patch方法

数据更新后,setter会触发patch方法,对比新旧vnode,然后进行视图更新

(1)通过isSameVnode方法判断是否为同一节点

a.vnode的tag和key值同时相等时,认定为相同节点
b.如果不是同一节点,删除旧节点,然后插入新节点
vue中虚拟dom和diff - 图7