前端开发--快速了解Vue中的diff算法

发布于:2023-01-22 ⋅ 阅读:(7) ⋅ 点赞:(0) ⋅ 评论:(0)

#博学谷IT学习技术支持#

目录

diff算法的概念

diff算法的三种比较方式

方式1: 根元素变了, 删除重建

方式2: 根元素没变, 属性改变, ==元素复用==, 更新属性

方式三: 根元素没变, 子元素没变, 元素内容改变

无key - 就地更新

有key, 值为索引

有key - 值为id

 总结

diff算法的概念

vue文件中的template里写的标签, 都是模板, 都要被vue处理成虚拟DOM对象, 才会渲染显示到真实DOM页面上

比如template里标签结构

<template>
    <div id="box">
        <p class="my_p">123</p>
    </div>
</template>

对应的虚拟DOM结构

const dom = {
    type: 'div',
    attributes: [{id: 'box'}],
    children: {
        type: 'p',
        attributes: [{class: 'my_p'}],
        text: '123'
    }
}

内存中生成一样的虚拟DOM结构(==本质是个JS对象==)

因为真实的DOM属性好几百个, 没办法快速的知道哪个属性改变了

利用diff算法, 找不不同, 只更新变化的部分(重绘/回流)到页面 - 也叫打补丁

生成新的虚拟DOM结构和旧的虚拟DOM结构对比

==好处1: 提高了更新DOM的性能(不用把页面全删除重新渲染)==

==好处2: 虚拟DOM只包含必要的属性(没有真实DOM上百个属性)==

虚拟DOM保存在内存中, 只记录dom关键信息, 配合diff算法提高DOM更新的性能

在内存中比较差异, 然后给真实DOM打补丁更新上

diff算法的三种比较方式

方式1: 根元素变了, 删除重建

旧虚拟DOM

<div id="box">
    <p class="my_p">123</p>
</div>

新虚拟DOM

<ul id="box">
    <li class="my_p">123</li>
</ul>

方式2: 根元素没变, 属性改变, ==元素复用==, 更新属性

旧虚拟DOM

<div id="box">
    <p class="my_p">123</p>
</div>

新虚拟DOM

<div id="myBox" title="标题">
    <p class="my_p">123</p>
</div>

方式三: 根元素没变, 子元素没变, 元素内容改变

无key - 就地更新

v-for不会移动DOM, 而是尝试复用, 就地更新,如果需要v-for移动DOM, 你需要用特殊 attribute key 来提供一个排序提示

<ul id="myUL">
    <li v-for="str in arr">
        {{ str }} 
        <input type="text">
    </li>
</ul>
<button @click="addFn">下标为1的位置新增一个</button>
export default {
    data(){
        return {
            arr: ["老大", "新来的", "老二", "老三"]
        }
    },
    methods: {
        addFn(){
            this.arr.splice(1, 0, '新来的')
        }
    }
};

旧 - 虚拟DOM结构 和 新 - 虚拟DOM结构 对比过程  

==性能不高, 从第二个li往后都更新了==  

有key, 值为索引

还是就地更新,因为新旧虚拟DOM对比, key存在就复用此标签更新内容, 如果不存在就直接建立一个新的

<ul id="myUL">
    <li v-for="(str, index) in arr" :key="index">
        {{ str }} 
        <input type="text">
    </li>
</ul>
<button @click="addFn">下标为1的位置新增一个</button>
export default {
    data(){
        return {
            arr: ["老大", "新来的", "老二", "老三"]
        }
    },
    methods: {
        addFn(){
            this.arr.splice(1, 0, '新来的')
        }
    }
};

 

  1. v-for先循环产生新的DOM结构, key是连续的, 和数据对应

  2. 然后比较新旧DOM结构, 找到区别, 打补丁到页面上

    最后补一个li, 然后从第二个往后, 都要更新内容

有key - 值为id

key的值只能是唯一不重复的, 字符串或数值

v-for不会移动DOM, 而是尝试复用, 就地更新,如果需要v-for移动DOM, 你需要用特殊 attribute key 来提供一个排序提示

新DOM里数据的key存在, 去旧的虚拟DOM结构里找到key标记的标签, 复用标签

新DOM里数据的key存在, 去旧的虚拟DOM结构里没有找到key标签的标签, 创建

旧DOM结构的key, 在新的DOM结构里没有了, 则==移除key所在的标签==

<template>
  <div>
    <ul>
      <li v-for="obj in arr" :key="obj.id">
        {{ obj.name }}
        <input type="text">
      </li>
    </ul>
    <button @click="btn">下标1位置插入新来的</button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      arr: [
        {
          name: '老大',
          id: 50
        },
        {
          name: '老二',
          id: 31
        },
        {
          name: '老三',
          id: 10
        }
      ],
    };
  },
  methods: {
    btn(){
      this.arr.splice(1, 0, {
        id: 19, 
        name: '新来的'
      })
    }
  }
};
</script>

<style>
</style>

 不用key也不影响功能(就地更新), 添加key可以提高更新的性能

 总结

diff算法如何比较新旧虚拟DOM?

  • 根元素改变 – 删除当前DOM树重新建

  • 根元素未变, 属性改变 – 更新属性

  • 根元素未变, 子元素/内容改变

  • 无key – 就地更新 / 有key – 按key比较

key值要求是唯一不重复的字符串或者数值
key的值有id用id, 没id用索引

key的好处:可以配合虚拟DOM提高更新的性能