关于虚拟 DOM 和 DOM diff

151 阅读2分钟

1、虚拟 DOM 是什么

  • 一个能代表 DOM 树的对象,通常含有标签名、标签上的属性、事件监听和子元素们,以及其他属性。

2、虚拟 DOM 优点

  • 减少 DOM 操作
    • 虚拟 DOM 可以将多次操作合并为一次操作,比如添加 1000 个节点,需要一个一个操作
    • 虚拟 DOM 借助 DOM diff 可以把多余的操作省略掉,添加 1000 个节点时,其中只有 10 个是新增的,那么就不会去新增 1000个
  • 跨平台
    • 虚拟 DOM 本质上是一个 JS 对象,不仅可以变成 DOM,还可以变成小程序、IOS 应用、安卓应用

3、虚拟 DOM 缺点

  • 需要额外的创建函数,如 React 需要 createElement 或 Vue 需要 h,但可以通过 JSX 来简化成 XML 写法

4、虚拟 DOM 长什么样

  • React
const vNode = {  
    key: null,   
    props: {     
        children: [  // 子元素们        
            { type: 'span', ... },         
            { type: 'span', ... }     
        ],     
        className: "red" // 标签上的属性     
        onClick: () => {} // 事件   
    },   
    ref: null,   
    type: "div", // 标签名 or 组件名   
    ... 
}
  • Vue
const vNode = {  
    tag: "div", // 标签名 or 组件名   
    data: {     
        class: "red", // 标签上的属性     
        on: {       
            click: () => {} // 事件     
        }   
    },   
    children: [ // 子元素们     
        { tag: "span", ... },     
        { tag: "span", ... }   
    ],   
    ... 
}

5、DOM diff 的大概逻辑

  • Tree diff
    • 将新旧两棵树逐层对比,找出哪些节点需要更新
    • 如果节点是组件就看 Component diff
    • 如果节点是标签就看 Element diff
  • Component diff
    • 如果节点是组件,就先看组件类型
    • 类型不同直接替换(删除旧的)
    • 类型相同则只更新属性
    • 然后深入组件做 Tree diff(递归)
  • Element diff
    • 如果节点是原生标签,则看标签名
    • 标签名不同直接替换,相同则只更新属性
    • 然后进入标签后代做 Tree diff(递归)

6、DOM diff 存在的 bug

  • 同级节点对比存在 bug
    • 例如:
    • [1,2,3] 变成 [1,3],从人的角度看来是删除了这个数组的第2项,但是在计算机看来,则是分解成了两个操作:首先删除了第3项,然后把第二项的值改为3,这就是 DOM diff 存在的bug。
  • 如何解决?
    • 在 v-for 循环中,加入 key,对每一项进行标记,这样在对其进行相关操作时,计算机就能明白是对具体的哪一项进行的修改