虚拟DOM和Dom Diff

99 阅读4分钟

什么是虚拟Dom

虚拟Dom(Virtual dom),他是通过js的对象来模拟dom中的节点,然后再通过特定的render方式来将其渲染成真实的dom节点

在传统开发模型中,用原生的js或jq来操作dom,浏览器会经历构建dom树->生成样式表->根据dom树和样式表,构建一棵render树->根据render树浏览器开始布局,获得每个render树上节点的精准坐标->根据坐标,把节点绘制出来这样一整个过程,也就是说如果需要更新10个dom节点的话,浏览器在遇到一个dom节点就会走一遍上面的过程,然后循环10次,这是非常耗时的一个操作

但是有了虚拟dom以后,若是要更新10次dom节点的话,首先不会先进行dom操作,而是记录这10次的更新diff到一个本地的js对象上,然后在把这个对象放到dom树上进行dom操作,这样一来就节省了很多的时候

虚拟dom的优点

  1. 减少dom操作 如上述所说,虚拟dom可以将多次dom操作合并为一次,在传统开发中,增加1000个dom节点,就需要进行1000的dom操作,但是虚拟dom就可以借助dom diff把多余的操作省略,比如要增加1000个dom节点,但是只有10个是需要新增的
  2. 跨平台 因为虚拟dom不仅可以变成dom,还可以变成小程序,IOS应用或安卓应用,因为他的本质只是一个js对象

如何创建虚拟节点

vue中使用的是render函数中的h(),例如

h('div',{
class:'red',
on:{click:()=>{}}
},[h('span',{class:'green'},'span1')....])
<div class ='red' @click="fn">
<span class ="green">span1</span>
</div>

所以vue是通过vue-loader转换为h()的方式

虚拟dom的缺点

需要额外的创建函数,如vue中的h,但是在react中可以通过使用JSX来简化成XML写法

DOM diff是什么

dom diff就是一个函数patch,当节点更新的时候patches = patch(oldVNode,newVNode),那么patches就是要运行的dom操作 简单来讲就是在一棵dom树上的每一个节点做比较,如果是类型不同就替换,如果属性不同就更新,但是如果一个节点进行替换了,就不会再往深去做diff操作了,这在一定程度上也牺牲了一定的精确度,复杂了为O(n) 总结diff操作

  1. 用JS对象模拟DOM(虚拟DOM)
  2. 把此虚拟DOM转成真实DOM并插入页面中(render)
  3. 如果有事件发生修改了虚拟DOM,比较两棵虚拟DOM树的差异,得到差异对象(diff)
  4. 把差异对象应用到真正的DOM树上(patch)

diff的缺点

同级对比的时候会出现一些bug 如页面有a,b,c三个节点,当增加一个d节点在ab之间的时候,那么虚拟dom需要遍历整个新的dom节点与旧的节点做对比,那么旧的b的位置被d代替,所以就需要写在b增加d,卸载c,增加b,在增加c,这样效率就十分低了, 此外,如果有1:a,2:b,3:c三个节点,这里的数字只是代表index索引,表示几号的意思,若删除了b,原本剩下的应该是1:a,3:c,但是实际结果是1:a 3:b

template>
  <div id="app">
    <Child v-for="item,i in array" :text="item" @delete="remove(i)"/>
  </div>
</template>

<script>
import Child from "./components/Child";

export default {
  name: "App",
  components: {
    Child
  },
  data() {
    return {
      array: [1, 2, 3]
    };
  },
  methods: {
    remove(i) {
      this.array.splice(i, 1);
    }
  }
};
</script>

所以diff就是默认当你把2节点删掉了,那么是2节点变成了3节点,然后删掉了3节点,这时候因为2变成3是属性变化,只需要更新这一点就行了,里面的内容还是继续复用

解决办法:

在每个节点给他加上一个key即绑定:key,但是最好不要用index,否则还会出上上述情况

本文参考