VUE虚拟DOM以及diff算法及key的好处

目录

    • VDOM
    • 浏览器渲染页面的流程
    • VDOM操作
    • diff算法
    • key的引入

VDOM

虚拟DOM(Virtual DOM)就是常说的虚拟节点,它是通过JS的Object对象模拟DOM中的节点,然后通过特定的render方法将其渲染成真实的DOM的节点。

真实DOM的代码:

<div>
  <p>文本段p>
div>

VDOM的伪代码:

var Vnode = {
  tag: 'div',
  children: [{
    tag: 'p',
    text: '文本段'
  }]
}

为什么要引入虚拟DOM?
解决浏览器性能问题,减少重排,提高性能。

先了解一下浏览器渲染页面的流程。

浏览器渲染页面的流程

(1)HTML被HTML解析器解析成DOM Tree,CSS被CSS解析器解析成CSSOM Tree。(并行解析)
(2)DOM Tree和CSSOM Tree合并到一起,形成了render Tree(渲染树)。

重排:节点信息计算,即根据渲染树计算每个节点的几何信息(大小及位置)。
重绘:渲染绘制,即根据计算好的信息绘制整个页面,渲染出最终的页面。
前端性能优化——重排重绘.

总结: 每次真实 DOM 发生改变引起重排都会将上面的流程跑一遍,而重排过程特别是计算节点信息是非常消耗性能的,于是我们引入VDOM,在VDOM上进行的操作不会引起重排,然后再通过diff算法比较新VDOM(修改之后的)和旧 VDOM(修改前的)的不同从而去更新真实DOM(patch方法)。

注意: 特别要提一下 Vue 的 patch 是即时的,并不是打包所有修改最后一起操作DOM(React则是将更新放入队列后集中处理),这样岂不是相当于没有优化?实际上现代浏览器对这样的DOM操作做了优化,所以表现出来的结果是一样的,即减少的操作真实DOM的次数,达到减少重排,提高性能的目的。

VDOM操作

Vue在进行DOM渲染时,出于性能考虑,会尽可能的复用已经存在的元素,而不是重新创建新的元素。
所以呢,VUE先在内存建一个虚拟DOM(Virtual DOM,简写VDOM),先把需要渲染的DOM元素放在这里,然后再放到浏览器上。
VDOM存放DOM元素是这样的,它将近似的元素进行复用,而不是重新创建一个元素,所以当浏览器需要用到两个近似的DOM元素时,其实用的是同一个DOM元素,只是有些属性值会进行对比修改。

看一个小案例:

<div id="app">
  <span v-if="isUser">
    <label for="username">用户账号label>
    <input type="text" id="username" placeholder="用户账号">
  span>
  <span v-else>
    <label for="email">用户邮箱label>
    <input type="text" id="email" placeholder="用户邮箱">
  span>
  <button @click="isUser = !isUser">切换类型button>
div>

<script src="../js/vue.js">script>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      isUser: true
    }
  })
script>

当我们在用户账号的输入框内输入123时,突然想改类型,点击切换类型按钮时,输入框里面依旧是123。但是呢,此时的输入框id是email;而未切换之前的输入框的id是username。因为Vue内部会发现原来的input元素跟v-else中的input相似,因此直接拿来使用了。
在这里插入图片描述
在这里插入图片描述

如果我们不想重复使用这个input,只需使用key属性即可,但是key的属性值不能相同

<input type="text" id="username" placeholder="用户账号" key="username">

<input type="text" id="email" placeholder="用户邮箱" key="email">

diff算法

先看一个简单的例子:
五个li 分别显示A,B,C,D,E,当我们要在B、C之间插入一个F,可以利用数组的splice(2,0,‘F’)。但是VUE系统怎么实现显示A,B,F,C,D,E呢?
VUE虚拟DOM以及diff算法及key的好处_第1张图片
diff算法默认执行起来是这样的:
VUE虚拟DOM以及diff算法及key的好处_第2张图片
VUE虚拟DOM以及diff算法及key的好处_第3张图片
VUE虚拟DOM以及diff算法及key的好处_第4张图片
即把C更新成F,D更新成C,E更新成D,最后再插入E,是不是很没有效率?
这样太耗性能,如果CDE后面跟的是一大堆东西的话,就要改动一大堆的数据,而我们只是想在BC中间插入一个F,除了位置,现有的东西都不想改变。
所以我们需要使用key来给每个节点做一个唯一标识,Diff算法就可以正确的识别此节点,找到正确的位置区插入新的节点。

key的引入

key的作用主要是为了高效的更新虚拟DOM

<li v-for="item in letters" :key="item">{{item}}li>

引入key,并且将item赋值给它,这样每一个li都有一个唯一的标识符。
VUE虚拟DOM以及diff算法及key的好处_第5张图片
因为key不同,所以VDOM不会复用之前的li,而是创建一个新的li再放到浏览器上。

现在又有一个新的问题,就是将index赋值给key可以吗?
用 index 作为 key,和没写基本上没区别,因为不管你数组的顺序怎么颠倒,index 都是 0, 1, 2 …这样排列,导致 Vue 会复用错误的旧子节点,做很多额外的工作(类名、样式、指令,那么都会被全量的更新)。
VUE虚拟DOM以及diff算法及key的好处_第6张图片
在2,3,4中可以知道,key是相同的,所以VDOM会复用原先的li,但是他们所带的值却不同,所以又需要改变值,那这样跟没写key一样了。(看不懂可以再回去看diff的算法)

注: 用组件唯一的 id(一般由后端返回)作为它的 key,实在没有的情况下,可以在获取到列表的时候通过某种规则为它们创建一个 key,并保证这个 key 在组件整个命周期中都保持稳定。

代码:

<div id="app">
  <ul>
	<li v-for="(item,index) in letters" :key="item">{{item}}li>
  ul>
  <button @click="addF">加Fbutton>
div>
<script src="../js/vue.js">script>
<script>
  const app = new Vue({
	el: '#app',
    data: {
	  letters: ['A', 'B', 'C', 'D', 'E']
	},
    methods: {
	  addF() {
		this.letters.splice(2, 0, 'F');
      }
	}
  })
script>

你可能感兴趣的:(vue)