从:key的角度,来看Vue3中diff算法的实现原理(多图详解)

简介: 多图详解diff算法
Hi~,我是 一碗周,一个在舒适区垂死挣扎的前端,如果写的文章有幸可以得到你的青睐,万分有幸~

写在前面

在我们使用v-for指令的时候,尤大大建议我们为每一项都添加一个唯一的属性key,在Vue文档中是这么说的:

为了给Vue一个提示,以便它能跟踪每个节点的身份,从而重用和重新排序现有元素,你需要为每项提供一个唯一的keyattribute,除非遍历输出的 DOM 内容非常简单。

看到这,是不是会有以下疑问:

  • 为什么要添加一个唯一的key
  • 为什么不建议用索引作为唯一的key
  • 为什么不能使用随机数作为唯一的key

接下来我们依次回答这些问题。

key是什么

在文档中对key属性,解释是这样的:

  • key属性主要用在Vue的虚拟DOM算法提示,在对比新旧nodes时辨识VNodes;
  • 如果不使用key,Vue会使用一种算法最小化元素的移动并且尽可能的尝试就地修改/复用相同类型元素;
  • 而使用key时,它会基于key的变化重新排列元素顺序,并且会移除/销毁key不存在的元素。

看了这些内容就更加的懵了,又出现了新的问题:

  • 什么是Vnode?
  • 什么是虚拟DOM?
  • 没有key的时候,如何尝试修改和复用的?
  • key的时候,如何基于key重写排列?

我们接着看。

虚拟DOM

VNode

首先我们先来看一下VNode。VNode即Virtual Node,也就是虚拟节点,在Vue中无论是组件还是元素,最终表示出来的都是一个个VNode,VNode的本质其实就是一个JavaScript对象,例如下面这个节点:

<div class="name" style="font-size: 3rem; color: #333;">一碗周</div>

它最终的VNode是这样的:

const vNode = {
  type: 'div',
  props: {
    class: 'name',
    style: {
      fontSize: '3rem',
      color: '#333',
    },
  },
  children: '一碗周',
}

虚拟DOM

如果不是只有一个节点,而是有很多个节点,形成了一个VNode Tree,也就是虚拟DOM,例如下面这张图:

01_虚拟DOM.png

也就是说在Vue中写HTML代码,先会被转换为虚拟DOM,然后才会被渲染成真实DOM。

虚拟DOM VS 真实DOM

前面我们介绍了,Vue中渲染真实DOM一共需要三个步骤,而原生仅仅需要两个步骤,如下图:

02_DOM渲染过程.png

如果单从图上看的话,毫无悬念,直接生成真实DOM的方式更快一些。

但实际上,如果某个节点的内容发生变化时,比较虚拟DOM比直接比较真实DOM在性能上是要更优的,这也是为什么Vue、React等框架采用虚拟DOM的原因。

上面所说的比较,就是采用的diff算法。

不使用key属性的处理方式

现在我们来看一下不使用key属性是何如处理的,我们找到相关的源码,在packages\runtime-core\src\renderer.ts中,大概1600行的位置,这里面表明了存在key和不存在key分别调用哪个函数,如下图:

03_有key和没key的处理方式.png

现在我们直接定位到patchUnkeyedChildren()方法,看看在这个方法中做了些什么,如下图:

04_patchUnkeyedChildren解析.png

如果只看图不理解的话,咱们举一个例子,代码如下:

<body>
  <div id="app"></div>
  <template id="my-app">
    <ul class="list-group">
      <li class="list-group-item" v-for="item in list">{{item.name}}</li>
    </ul>
  </template>
  <script src="https://unpkg.com/vue@next"></script>
  <script>
    Vue.createApp({
      template: '#my-app',
      data() {
        return {
          list: [
            { id: 0, name: 'HTML' },
            { id: 1, name: 'CSS' },
            { id: 2, name: 'JavaScript' },
            { id: 3, name: 'Vue.js' },
            { id: 4, name: 'React.js' },
          ],
        }
      },
    }).mount('#app')
  </script>
</body>

运行结果如下:

05_demo1.png

现在我们在Vue.js前面插入一个{id: 5, name: 'Ajax'},其新旧虚拟DOM如下图所示:

06_demo1解析1.png

执行过程如下:

06_demo1解析2.png

如果old VDOMnew VDOM多的化,多余的部分直接卸载。

使用key属性的处理

使用key属性的处理方式是这篇文章的重头戏,我们首先看一下patchKeyedChildren()方法做了些什么,如下图:

07_patchKeyedChildren函数.png

我们可以看到,这个方法比较长,但是它分为5个步骤,我们依次来看:

tips:开发Vue框架的大佬都写注释,你还觉着不需要写注释吗????

我们依次来看,第一步,从头开始遍历,主要内容如下图:

07_patchKeyedChildren函数01.png

我们举个例子:

08_demo2解析01.png

首先key0的比较,发现一致,继续比较,直到key3key5的比较,不一致直接跳出这个while循环。

第二步的操作与第一步基本一致,只不过就是从尾部开始,主要内容如下:

09_patchKeyedChildren函数02.png

例子:

10_demo2解析.png

第三步和第四步,分别是添加新节点或者移除旧节点,主要代码如下:

11_patchKeyedChildren函数03.png

例子:

12_demo3解析.png

最后一步,也就是最复杂的异常,这个是用于处理中间的乱序,或者移动新增等问题,例子如下:

13_demo4解析.png

所以我们可以发现,Vue在进行diff算法的时候,会尽量利用key来优化操作,所以说key属性是必须的。

为什么不能使用索引作为key

如果我们是使用index作为key,假如有下面这个数组:

list: [
  {name: 'HTML'},
  {name: 'CSS'},
  {name: 'JS'},
]

我们在最前面添加一项:

list.unshift({name: '一碗周'})

如果使用索引做key的话,会出现下面这种问题:

14_使用index作为key.png

所以说我们尽量要使用唯一的且不变的值作为key

注意:千万不要使用随机数作为key,不然每次比较都不相同

写在最后

以上就是这篇文章的全部内容,如果那里有错误,欢迎指正;如果帮助到你,可以帮我点个赞支持一下,毕竟画图不易~

目录
相关文章
|
27天前
|
JavaScript 前端开发 API
介绍一下Vue中的响应式原理
介绍一下Vue中的响应式原理
30 1
|
1月前
|
监控 JavaScript 算法
深度剖析 Vue.js 响应式原理:从数据劫持到视图更新的全流程详解
本文深入解析Vue.js的响应式机制,从数据劫持到视图更新的全过程,详细讲解了其实现原理和运作流程。
|
1月前
|
JavaScript
Vue 双向数据绑定原理
Vue的双向数据绑定通过其核心的响应式系统实现,主要由Observer、Compiler和Watcher三个部分组成。Observer负责观察数据对象的所有属性,将其转换为getter和setter;Compiler解析模板指令,初始化视图并订阅数据变化;Watcher作为连接Observer和Compiler的桥梁,当数据变化时触发相应的更新操作。这种机制确保了数据模型与视图之间的自动同步。
|
1月前
|
缓存 JavaScript 搜索推荐
Vue SSR(服务端渲染)预渲染的工作原理
【10月更文挑战第23天】Vue SSR 预渲染通过一系列复杂的步骤和机制,实现了在服务器端生成静态 HTML 页面的目标。它为提升 Vue 应用的性能、SEO 效果以及用户体验提供了有力的支持。随着技术的不断发展,Vue SSR 预渲染技术也将不断完善和创新,以适应不断变化的互联网环境和用户需求。
74 9
|
27天前
|
JavaScript 前端开发 API
Vue.js响应式原理深度解析:从Vue 2到Vue 3的演进
Vue.js响应式原理深度解析:从Vue 2到Vue 3的演进
54 0
|
3月前
|
缓存 JavaScript 前端开发
「offer来了」从基础到进阶原理,从vue2到vue3,48个知识点保姆级带你巩固vuejs知识体系
该文章全面覆盖了Vue.js从基础知识到进阶原理的48个核心知识点,包括Vue CLI项目结构、组件生命周期、响应式原理、Composition API的使用等内容,并针对Vue 2与Vue 3的不同特性进行了详细对比与讲解。
「offer来了」从基础到进阶原理,从vue2到vue3,48个知识点保姆级带你巩固vuejs知识体系
|
2月前
|
算法 JavaScript UED
Diff 算法的实现原理
【10月更文挑战第18天】Diff 算法是 Vue.js 中实现高效 DOM 更新的核心机制,通过合理的比较和优化策略,能够在保证界面正确性的同时,最大程度地减少 DOM 操作,提高应用的性能和用户体验。
36 2
|
2月前
|
算法 JavaScript
Vue 中的 Diff 算法
【10月更文挑战第18天】需要注意的是,Diff 算法虽然能够提高性能,但在某些复杂的场景下,可能仍然会存在一些性能瓶颈。因此,在实际开发中,我们需要根据具体情况合理地使用 Diff 算法,并结合其他优化手段来提高应用的性能。
19 1
|
2月前
|
JavaScript 算法 前端开发
vue 中diff算法
【10月更文挑战第10天】
41 1
|
2月前
|
JavaScript UED
Vue双向数据绑定的原理
【10月更文挑战第7天】
下一篇
DataWorks