Vue2 - diff 原理(动图演示)

目录

  • 1,diff
    • diff 的时间点
  • 2,_update 函数
  • 3,_patch 函数(进行 diff)
    • 3.1,根节点比较
    • 3.2,子节点比较
  • 4,key的问题
    • 举例1
    • 举例2

1,diff

解释:对比新旧虚拟DOM树,完成对真实DOM的更新,这个对比差异的过程叫做 diff

Vue 会在内部的 patch 函数中完成该过程。

diff 的时间点

当组件创建时,或依赖的数据变化时,会运行一个特定的函数来做2件事:

  • 运行 _render 函数生成新的 VNode tree(虚拟DOM树)
  • 运行 _update 函数,传入新的 VNode tree 的根节点,对比新旧2个树,最终完成对真实DOM的更新。

代码表示大致逻辑:

// vue构造函数
function Vue(){
  // ... 其他代码
  var updateComponent = () => {
    this._update(this._render())
  }
  new Watcher(updateComponent);
  // ... 其他代码
}

diff 就发生在_update函数的运行过程中

Watcher 的作用:简单来说,运行传入的函数(updateComponent),对函数中用到的响应式数据进行依赖收集。

Watcher 的作用具体参考Vue2-数据响应式原理

2,_update 函数

  1. _update 函数接收一个 VNode 参数,也就是this._render() 返回的生成的虚拟 DOM 树。

  2. _update 函数通过当前组件的 this._vnode 属性,拿到的虚拟 DOM 树。

  3. _update 函数首先会给组件的 this._vnode 属性重新赋值,让它指向新树。再判断旧树是否存在:

    • 不存在,说明是第一次加载组件,则通过 patch 函数直接遍历新树,为每个节点生成真实的DOM,并挂载到每个节点的 elm 属性上。(虚拟节点通过 elm 属性指向绑定的真实DOM。)
    • 存在,说明之前已经渲染过组件,则通过 patch 函数对新旧树对比,来实现2个目标:
      • 完成对所有真实 DOM 的最小化处理。
      • 让新树的节点对应合适的真实DOM。

不存在的流程:
不存在的流程
存在时的流程:
不存在时的流程

// 伪代码表示:
function update(vnode) {
  vnode // 新
  this._vnode // 旧
  this._vnode = vnode
}

这样就完成了组件的虚拟DOM树的更新

但还需要解决真实的 DOM 更新(如果不考虑效率,直接用新树生成真实DOM即可)。而为了提升效率,需要对比新旧树,通过实现下面2个目标来提升效率。这个步骤在 _patch 函数中实现。

  • 完成对所有真实 DOM 的最小化处理。
  • 让新树的节点对应合适的真实DOM。

3,_patch 函数(进行 diff)

先来介绍几个术语,方便后续阅读:

  1. 【相同】:指2个虚拟节点的标签(tag)类型、key 值均相同。input 元素还需要考虑 type 属性。

不考虑内容,或后代节点。

<!-- 举例 -->

<!-- 节点相同 -->
<h1>123</h1> <!-- 对应节点 { tag: h1, key: undefined } -->
<h1>456</h1> <!-- 对应节点 { tag: h1, key: undefined } -->

<!-- 节点相同 -->
没有标签包裹的文字1  <!-- 对应节点 { tag: undefined , key: undefined } -->
没有标签包裹的文字2  <!-- 对应节点 { tag: undefined , key: undefined } -->

<!-- 节点相同 -->
<h1>123</h1> <!-- 对应节点 { tag: h1, key: undefined } -->
<h1>456</h1> <!-- 对应节点 { tag: h1, key: undefined } -->

<!-- 节点不同 -->
<input type="text" key="_key1"> <!-- 对应节点 { tag: input, key: _key1, data: {attrs: {type: text}} } -->
<input type="radio" key="_key1"> <!-- 对应节点 { tag: input, key: _key1, data: {attrs: {type: radio}} } -->
  1. 【新建元素】:根据一个虚拟节点提供的信息,创建一个真实的 DOM 元素,同时挂载到虚拟节点的 elm 属性上。

  2. 【销毁元素】:运行 vnode.elm.remove()

  3. 【更新】:2个虚拟节点进行对比更新,仅发生在2个虚拟节点【相同】的情况下。

  4. 【对比子节点】:对2个虚拟节点的子节点进行对比。

3.1,根节点比较

在这里插入图片描述
首先会对根节点比较,如果2个虚拟节点

【相同】:进入【更新】流程

  • 将旧节点的真实 DOM 赋值到新节点:newVNode.elm = oldVNode.elm
  • 对比新旧节点的属性,有变化的更新到真实 DOM 中。
  • 当前2个节点处理完毕,开始【对比子节点】

不【相同】:新节点递归的【新建元素】。旧节点直接【销毁元素】。

如果根节点都不相同,则没有对比的必要,直接当做旧树不存在处理。

3.2,子节点比较

diff 的重点

再说明下 diff 的目的:为了修改真实的 DOM,并和新的 VNode tree 对应上

在【对比】子节点时,vue 的实现思路:

  1. 尽量什么也不做。
  2. 不行的话,尽量只改动元素属性。
  3. 还不行,尽量移动元素,而不是删除或创建元素。
  4. 还不行,删除和创建元素。

实现大致逻辑:使用头尾指针+遍历来实现。动图演示(数字代表的是 key,蓝块中的数字代表真实DOM的内容):

在这里插入图片描述

  • 对比新旧指针
    • 一样则进入【更新】流程。
      • 顺序:新旧头指针,新旧尾指针,旧头和新尾,旧尾和新头。
    • 不一样,则以新头指针为基准,看对应的 key 在旧树中是否存在(通过遍历旧树的方式),
      • 存在则进入【更新】流程,并调整真实 DOM 的位置,移动新头指针。
      • 不存在,则创建节点对应的 DOM 元素,
  • 当新头指针超过新尾指针,循环结束。剩下的旧节点如果还是正常的,说明没有处理完,则遍历销毁所有节点对应的真实DOM。旧树不用管,会被垃圾回收。

注意,每个新旧节点【更新】时,都会递归的遍历子节点。

4,key的问题

举例1

for 循环中的 item 如果不使用 key,数据更新(尤其是位置发生了变化)后做 diff 时,会认为原来位置新旧头指针每次指向的虚拟节点都【相同】,则每个节点都会【更新】。如果子节点较多,效率就更低了。

举例:

<template>
  <div>
    <ul>
      <li v-for="item in arr" :key="item">{{ item }}</li>
    </ul>
    <button @click="arr.unshift(99 + count++)">头部插入</button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      arr: [1, 2, 3, 4, 5],
      count: 0,
    };
  },
};
</script>

因为 key 的存在,

  • 翻转数组时,也只是位置的移动,不会对比内容更新。
  • 头部插入时,只创建一个DOM,其他的DOM不做变动。否则逐一对比更新。

key 的效果:

在这里插入图片描述

不加 key 的效果:

在这里插入图片描述

举例2

注意,v-if/v-else 关于 key 的问题,vue3 会自动添加,可以看这篇文章对比 vue2 和 vue3 的变化。

<template>
  <div>
    <div>
      <span @click="isAccoutLogin = true">账号登录</span>
      <span>|</span>
      <span @click="isAccoutLogin = false">手机号登录</span>
    </div>
    <div v-if="isAccoutLogin" key="1">
      <label>账号</label>
      <input type="text" />
    </div>
    <div v-else key="2">
      <label>手机号</label>
      <input type="text" />
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      isAccoutLogin: true,
    };
  },
};
</script>

不加 key 时,因为节点相同,子节点也相同,所以不做更新。

在这里插入图片描述

key 才会有所区分,而清空输入框。

在这里插入图片描述


以上。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/289788.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

很实用的ChatGPT网站—在线编程模块增补篇

很实用的ChatGPT网站&#xff08;http://chat-zh.com/&#xff09;——增补篇 今天介绍一个好兄弟开发的ChatGPT网站&#xff0c;网址[http://chat-zh.com/]。这个网站功能模块很多&#xff0c;包含生活、学习、医疗、法律、经济等很多方面。今天跟大家分享一下&#xff0c;新…

探索模块化神经网络在现代人工智能中的功效和应用

一、介绍 在快速发展的人工智能领域&#xff0c;模块化神经网络 (MNN) 已成为一项关键创新。与遵循整体方法的传统神经网络架构不同&#xff0c;MNN 采用分散式结构。本文深入探讨了 MNN 的基础知识、它们的优势、应用以及它们带来的挑战。 evertongomede 在人工智能领域&#…

【Java期末】学生成绩管理系统

诚接计算机专业编程任务(C语言、C、Python、Java、HTML、JavaScript、Vue等)10/15R&#xff0c;如有需要请私信我&#xff0c;或者加我的企鹅号&#xff1a;1404293476 本文资源下载地址&#xff1a;https://download.csdn.net/download/weixin_47040861/88697244 —————…

imgaug库指南(一):从入门到精通的【图像增强】之旅

文章目录 引言imgaug简介安装和导入imgaug代码示例imgaug的强大之处和用途小结结尾 引言 在深度学习和计算机视觉的世界里&#xff0c;数据是模型训练的基石&#xff0c;其质量与数量直接影响着模型的性能。然而&#xff0c;获取大量高质量的标注数据往往需要耗费大量的时间和…

Spark---RDD算子(单值类型Value)

文章目录 1.RDD算子介绍2.转换算子2.1 Value类型2.1.1 map2.1.2 mapPartitions2.1.3 mapPartitionsWithIndex2.1.4 flatMap2.1.5 glom2.1.6 groupBy2.1.7 filter2.1.8 sample2.1.9 distinct2.1.10 coalesce2.1.11 repartition2.1.12 sortBy 1.RDD算子介绍 RDD算子是用于对RDD进…

【数据结构】循环队列(数组实现)

目录 一、循环队列定义 怎么使一个数组在逻辑上呈“环状”呢&#xff1f; 二、循环队列与顺序队列的差异 1、存储方式: 2、操作方式: 3、空间利用率&#xff1a; 4、循环队列判断队空的方式&#xff1a; 5、循环队列判断队满的方式 完整测试代码及注释&#xff1a; 总…

Vue 中的 ref 与 reactive:让你的应用更具响应性(上)

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…

用HTML的原生语法实现两个div子元素在同一行中排列

代码如下&#xff1a; <div id"level1" style"display: flex;"><div id"level2-1" style"display: inline-block; padding: 10px; border: 1px solid #ccc; margin: 5px;">这是第一个元素。</div><div id"…

计算机系统基础

C 语言相关内容省略&#xff0c;复习自用&#xff0c;仅供参考~ 概述 冯诺伊曼结构 存储程序工作方式&#xff1a;将事先编好的程序和原始数据送入主存后才能执行程序&#xff0c;程序被启动执行后&#xff0c;计算机能在不需要操作人员干预下自动完成逐条指令取出和执行的任…

解析为什么Go语言要使用[]rune而不是string来表示中文字符

众所周知&#xff0c;Go语言中有以下这些数据类型。但rune32这个go语言特有的数据类型&#xff0c;比较有意思却经常遭到忽视。所以今天探索学习一下这个数据类型的功能、用法。 Go基本数据类型 布尔&#xff1a;bool 字符串&#xff1a;string 整数&#xff1a; int int8 …

NNDL 作业13 优化算法3D可视化 [HBU]

老师作业原博客:【23-24 秋学期】NNDL 作业13 优化算法3D可视化-CSDN博客 NNDL 作业13 优化算法3D可视化-CSDN博客 编程实现优化算法&#xff0c;并3D可视化 1. 函数3D可视化 分别画出 和 的3D图 NNDL实验 优化算法3D轨迹 鱼书例题3D版_优化算法3d展示-CSDN博客 代码&#…

JSON网络令牌JWT

1.什么是身份验证 日常生活中的身份验证的场景: 比如进入公司的大楼时&#xff0c;需要携带工牌&#xff1b;打卡上班时&#xff0c;需要指纹识别&#xff1b;打开工作电脑时&#xff0c;需要输入密码。 2. 什么是 JSON 网络令牌&#xff1f; JSON Web Token (JWT) 是一个开…

智能编程助手!华为云CodeArts Snap免费公测:基于盘古研发大模型

近日&#xff0c;华为云CodeArts Snap正式开启公测。 这是一款基于华为云研发大模型的智能化编程助手&#xff0c;旨在为开发者提供高效且智能的编程体验&#xff0c;提升研发人员的单兵作战能力。 该服务公测期间免费&#xff0c;不向用户收取任何费用&#xff0c;商用后&am…

【论文阅读|冷冻电镜】DISCA: High-throughput cryo-ET structural pattern mining

论文题目 High-throughput cryo-ET structural pattern mining by unsupervised deep iterative subtomogram clustering 摘要 现有的结构排序算法的吞吐量低&#xff0c;或者由于依赖于可用模板和手动标签而固有地受到限制。本文提出了一种高吞吐量的、无需模板和标签的深度…

【C++入门到精通】function包装器 | bind() 函数 C++11 [ C++入门 ]

阅读导航 引言一、function包装器1. 概念2. 基本使用3. 逆波兰表达式求值&#xff08;1&#xff09;普通写法&#xff08;2&#xff09;使用包装器以后的写法 二、bind() 函数温馨提示 引言 很高兴再次与大家分享关于 C11 的一些知识。在上一篇文章中&#xff0c;我们讲解了 c…

Vue前端文字效果:如何让一段文本像是手动一个一个字打出来的

效果展示 自己做的AI聊天机器人界面&#xff0c;我觉得比微信还好看 由于这个前端略微复杂&#xff0c;下文用最简单的例子来展示&#xff1a; 分析需求 对于AI聊天工具的前端&#xff0c;如果AI生成的文本像是一个一个字打出来的&#xff0c;就会让AI看起来更像真的人&…

打造炫酷粒子效果的前端利器tsParticles

前端潮流速递 &#xff1a;打造炫酷粒子效果的前端利器tsParticles 在现代前端开发中&#xff0c;动画和视觉效果是吸引用户的关键元素之一。而实现炫酷而引人入胜的粒子效果&#xff0c;常常需要耗费大量的时间和精力。然而&#xff0c;有了 tsParticles&#xff0c;这一切变…

MySQL 8.0 开关 Redo Logging

一 前言 前几天有客户测试使用云数据库的时候提出 要禁止mydumper 关闭redo log的操作 (说白了就是导入数据时保持MySQL 实例的redo logging功能)&#xff0c; 这才想起 在 MySQL 8.0.21 版本中&#xff0c;开启了一个新特性 “Redo Logging 动态开关”。 在新实例导数据的场…

搭建宠物寄养小程序流程

近日&#xff0c;一地宠物寄养需求旺盛&#xff0c;元旦满房&#xff0c;春节几近饱和&#xff0c;一窝难求。随着市场需求的增长&#xff0c;对于很多宠物行业的商家&#xff0c;可以考虑开展宠物寄养服务&#xff0c;尤其是节假日的宠物寄养需求会更高。因此&#xff0c;商家…

FastApi-快速入门1

FastAPI 是一个用于构建 API 的现代、快速&#xff08;高性能&#xff09;的 web 框架&#xff0c;使用 Python 3.8 并基于标准的 Python 类型提示。 关键特性: 快速&#xff1a;可与 NodeJS 和 Go 并肩的极高性能&#xff08;归功于 Starlette 和 Pydantic&#xff09;。最快…