当前位置: 首页 > news >正文

【Vue原理解析】之虚拟DOM

Vue.js是一款流行的JavaScript框架,它采用了虚拟DOM(Virtual DOM)的概念来提高性能和开发效率。虚拟DOM是Vue.js的核心之一,它通过在内存中构建一个轻量级的DOM树来代替直接操作真实的DOM,从而减少了对真实DOM的操作次数,提高了页面渲染效率。本文将深入探讨Vue.js中虚拟DOM的作用、核心源码分析。

虚拟DOM的作用

虚拟DOM是一个轻量级的JavaScript对象,它以树形结构表示整个页面的结构和状态。当页面发生变化时,Vue.js会通过比较新旧两个虚拟DOM树之间的差异,并将差异应用到真实的DOM上,从而更新页面。这种方式相比直接操作真实DOM具有以下几个优势:

1. 提高性能

由于直接操作真实DOM需要频繁地进行重排和重绘,而虚拟DOM可以批量更新差异,减少了对真实DOM的操作次数,从而提高了页面渲染效率。

2. 简化开发

通过使用虚拟DOM,开发者可以将关注点从手动操作真实DOM转移到更高层次的逻辑上。开发者只需要关注页面的结构和状态,而不需要关心具体的DOM操作细节,从而简化了开发流程。

3. 跨平台支持

由于虚拟DOM是一个独立于平台的JavaScript对象,因此可以在不同的平台上使用。这意味着开发者可以使用相同的代码库来构建Web、移动和桌面应用程序。

源码分析

在Vue.js中,虚拟DOM是通过VNode(Virtual Node)对象来表示的。VNode对象是一个纯JavaScript对象,它包含了节点的标签名、属性、子节点等信息。Vue.js通过递归地遍历VNode树来构建真实DOM,并通过比较新旧两个VNode树之间的差异来更新页面。

patch函数定义在src/core/vdom/patch.js文件中。它是将新的VNode对象应用到旧的VNode对象上,从而更新页面的核心函数。下面是patch函数的部分代码:

 
export function patch(oldVnode, vnode, hydrating, removeOnly) {// ...const isRealElement = isDef(oldVnode.nodeType)if (!isRealElement && sameVnode(oldVnode, vnode)) {// 如果新旧 VNode 是相同节点,则调用 patchVnode 函数进行比较和更新patchVnode(oldVnode, vnode, insertedVnodeQueue, null, null, removeOnly)} else {// 如果新旧 VNode 不是相同节点,则销毁旧节点并创建新节点const oldElm = oldVnode.elmconst parentElm = nodeOps.parentNode(oldElm)createElm(vnode,insertedVnodeQueue,oldElm._leaveCb ? null : parentElm,nodeOps.nextSibling(oldElm));// destroy old nodeif (isDef(parentElm)) {removeVnodes([oldVnode], 0, 0)} else if (isDef(oldVnode.tag)) {invokeDestroyHook(oldVnode)}}// ...
}

patch函数中,首先通过调用 sameVnode 函数判断新旧 VNode 是否为相同节点。如果是相同节点,则调用 patchVnode 函数进行比较和更新;如果不是相同节点,则销毁旧节点并创建新节点。

接下来,我们来看一下 patchVnode 函数的部分代码:

 
function patchVnode(oldVnode, vnode, insertedVnodeQueue, removeOnly) {// ...if (isDef(data) && isPatchable(vnode)) {// 比较和更新属性for (i = 0; i < cbs.update.length; ++i) cbs.update[i](oldVnode, vnode);if (isDef((i = data.hook)) && isDef((i = i.update))) i(oldVnode, vnode);}if (isUndef(vnode.text)) {if (isDef(oldCh) && isDef(ch)) {// 比较和更新子节点if (oldCh !== ch) updateChildren(elm, oldCh, ch, insertedVnodeQueue, removeOnly);} else if (isDef(ch)) {// 添加新的子节点// ...addVnodes(elm, null, ch, 0, ch.length - 1, insertedVnodeQueue);} else if (isDef(oldCh)) {// 移除旧的子节点removeVnodes(elm, oldCh, 0, oldCh.length - 1);}} else if (oldVnode.text !== vnode.text) {// 更新文本内容nodeOps.setTextContent(elm, vnode.text);}// ...
}

patchVnode函数中,首先比较和更新 VNode 的属性。通过遍历 cbs.update 数组,调用相应的更新函数来比较和更新属性。如果 VNode 不是文本节点,则比较和更新子节点。通过调用 updateChildren 函数来比较和更新新旧子节点。最后,如果 VNode 是文本节点,则直接更新文本内容。

通过以上代码,我们可以看到在 Vue.js 源码中,通过 patch 函数和 patchVnode 函数来比较和更新新旧 VNode 的差异。在比较过程中,会根据 VNode 的类型进行不同的处理,包括属性的比较和更新、子节点的比较和更新、文本内容的更新等。

这种差异比较的方式可以高效地将新的 VNode 对象应用到旧的 VNode 对象上,并将差异应用到真实 DOM 上,从而实现虚拟 DOM 的更新和渲染。这样可以减少对真实 DOM 的操作次数,提高页面渲染效率。

在更新页面时,Vue采用了一种高效的算法来比较新旧两个VNode树之间的差异。该算法将VNode树转换为一个补丁(Patch)数组,补丁数组中包含了需要对真实DOM进行操作的指令。然后,Vue.js通过遍历补丁数组,并根据指令对真实DOM进行相应的操作,从而更新页面。

简单示例说明

旧VNode: <div id="old">Hello, Vue!</div>

新VNode: <div id="new">Hello, Vue.js!<span>Extra content</span></div>

  1. 首先,将旧VNode和新VNode进行比较。比较标签名和属性。

    标签名相同,属性不同:

    • 旧VNode的id属性为"old"
    • 新VNode的id属性为"new"`
  2. 将差异添加到补丁数组中。

    补丁数组: [{ type: 'props', prop: 'id', value: 'new' }]

  3. 比较子节点。

    子节点不同:

    • 旧VNode有一个文本节点:"Hello, Vue!"
    • 新VNode有一个文本节点:"Hello, Vue.js!",以及一个子节点<span>Extra content</span>
  4. 将差异添加到补丁数组中。

    补丁数组:

     
    [{ type: 'props', prop: 'id', value: 'new' },{ type: 'text', value: 'Hello, Vue.js!' },{ type: 'insert', parentElm: ..., refElm: ..., vnode: ... }
    ]
    

通过以上示例,我们可以看到在比较新旧VNode时,会逐个比较它们的标签名、属性和子节点,并将差异添加到补丁数组中。这个补丁数组描述了新旧VNode之间的差异,可以用于后续的更新操作。

总结

虚拟DOM是Vue.js中一个重要且核心的概念。它通过在内存中构建一个轻量级的DOM树来代替直接操作真实的DOM,从而提高了性能和开发效率。虚拟DOM的核心源码分析揭示了Vue.js是如何通过比较新旧两个VNode树之间的差异来更新页面的。通过深入理解虚拟DOM的原理,开发者可以更好地利用Vue.js提供的功能和特性,从而构建高性能和可维护的Web应用程序。

相关文章:

【Vue原理解析】之虚拟DOM

Vue.js是一款流行的JavaScript框架&#xff0c;它采用了虚拟DOM&#xff08;Virtual DOM&#xff09;的概念来提高性能和开发效率。虚拟DOM是Vue.js的核心之一&#xff0c;它通过在内存中构建一个轻量级的DOM树来代替直接操作真实的DOM&#xff0c;从而减少了对真实DOM的操作次…...

HCIE-灾备技术和安全服务

灾备技术 灾备包含两个概念&#xff1a;容灾、备份 备份是为了保证数据的完整性&#xff0c;数据不丢失。全量备份、增量备份&#xff0c;备份数据还原。 容灾是为了保证业务的连续性&#xff0c;尽可能不断业务。 快照&#xff1a;保存的不是底层块数据&#xff0c;保存的是逻…...

【图论实战】Boost学习 01:基本操作

文章目录 头文件图的构建图的可视化基本操作 头文件 #include <boost/graph/adjacency_list.hpp> #include <boost/graph/graphviz.hpp> #include <boost/graph/properties.hpp> #include <boost/property_map/property_map.hpp> #include <boost/…...

Rust 中的引用与借用

目录 1、引用与借用 1.1 可变引用 1.2 悬垂引用 1.3 引用的规则 2、slice 类型 2.1 字符串字面量其实就是一个slice 2.2 总结 1、引用与借用 在之前我们将String 类型的值返回给调用函数&#xff0c;这样会导致这个String会被移动到函数中&#xff0c;这样在原来的作用域…...

Azure 机器学习:在 Azure 机器学习中使用 Azure OpenAI 模型

目录 一、环境准备二、Azure 机器学习中的 OpenAI 模型是什么&#xff1f;三、在机器学习中访问 Azure OpenAI 模型连接到 Azure OpenAI部署 Azure OpenAI 模型 四、使用自己的训练数据微调 Azure OpenAI 模型使用工作室微调微调设置训练数据自定义微调参数部署微调的模型 使用…...

XML Web 服务 Eclipse实现中的sun-jaxws.xml文件

说明 在sun-jaxws.xml文件&#xff0c;可以配置endpoint、handler-chain等内容。在这个文件中配置的内容会覆盖在Java代码中使用注解属性配置的的内容。 这个文件根据自己的项目内容修改完成以后&#xff0c;作为web应用的一部分部署到web容器中&#xff08;放到web应用的WEB…...

16.1 二次根式 教学设计及课堂检测设计

课堂检测如下&#xff1a;...

Android数据流的狂欢:Channel与Flow

在 Android 应用程序的开发中&#xff0c;处理异步数据流是一个常见的需求。为了更好地应对这些需求&#xff0c;Kotlin 协程引入了 Channel 和 Flow&#xff0c;它们提供了强大的工具来处理数据流&#xff0c;实现生产者-消费者模式&#xff0c;以及构建响应式应用程序。 本文…...

Java 单元测试最佳实践:如何充分利用测试自动化

单元测试是众所周知的做法&#xff0c;但还有很大的改进空间&#xff01;在这篇文章中&#xff0c;我们讨论最有效的单元测试最佳实践&#xff0c;包括在此过程中最大化自动化工具的方法。我们还将讨论代码覆盖率、模拟依赖关系和整体测试策略。 什么是单元测试&#xff1f; 单…...

windows系统用于 SDN 的软件负载均衡器 (SLB)

适用于&#xff1a;Azure Stack HCI 版本 22H2 和 21H2&#xff1b;Windows Server 2022、Windows Server 2019、Windows Server 2016 软件负载均衡器包括哪些内容&#xff1f; 软件负载均衡器提供以下功能&#xff1a; 适用于北/南和东/西 TCP/UDP 流量的第 4 层 (L4) 负载均…...

漏洞复现--IP-guard flexpaper RCE

免责声明&#xff1a; 文章中涉及的漏洞均已修复&#xff0c;敏感信息均已做打码处理&#xff0c;文章仅做经验分享用途&#xff0c;切勿当真&#xff0c;未授权的攻击属于非法行为&#xff01;文章中敏感信息均已做多层打马处理。传播、利用本文章所提供的信息而造成的任何直…...

Electron-vue出现GET http://localhost:9080/__webpack_hmr net::ERR_ABORTED解决方案

GET http://localhost:9080/__webpack_hmr net::ERR_ABORTED解决方案 使用版本解决方案解决总结 使用版本 以下是我解决此问题时使用的electron和vue等的一些版本信息 【附】经过测试 electron 的版本为 13.1.4 时也能解决 解决方案 将项目下的 .electron-vue/dev-runner.js…...

Linux---(六)自动化构建工具 make/Makefile

文章目录 一、make/Makefile二、快速查看&#xff08;1&#xff09;建立Makefile文件&#xff08;2&#xff09;编辑Makefile文件&#xff08;3&#xff09;解释&#xff08;4&#xff09;效果展示 三、背后的基本知识、原理&#xff08;1&#xff09;如何清理对应的临时文件呢…...

谷歌:编写干净的代码以减少认知负荷

您是否曾经阅读过代码却发现很难理解&#xff1f;您可能正在经历认知负荷&#xff01; 认知负荷是指完成一项任务所需的脑力劳动量。阅读代码时&#xff0c;您必须记住变量值、条件逻辑、循环索引、数据结构状态和接口契约等信息。随着代码变得更加复杂&#xff0c;认知负荷也…...

微信小程序display常用属性和子元素排列方式介绍

wxss中display常用显示属性与css一致&#xff0c;介绍如下&#xff1a; 针对元素本身显示的属性&#xff1a; displayblock&#xff0c;元素显示换行displayinline&#xff0c;元素显示换行&#xff0c;但不可设置固定的宽度和高度&#xff0c;也不可设置上下方向的margin和p…...

设计模式—结构型模式之代理模式

设计模式—结构型模式之代理模式 代理模式(Proxy Pattern) ,给某一个对象提供一个代理&#xff0c;并由代理对象控制对原对象的引用,对象结构型模式。 静态代理 比如我们有一个直播平台&#xff0c;提供了直播功能&#xff0c;但是如果不进行美颜&#xff0c;可能就比较冷清…...

C# PDF转HTML字符串

需要nuget安装Aspose.PDF插件&#xff0c;本文使用23.10.0版本 一、获取PDF文件&#xff0c;保存到某个路径&#xff1b;再读取返回字符串。 //html文件保存路径 string filePath dirPath "xxx.html"; if (!File.Exists(filePath)) {//获取pdf文件流Byte[] pdfBy…...

el-table解决数据过少小于高度有留白的问题

问题:给el-table设置个高度,高度为500px,之后就添加如下4条数据,那么底部就没数据,直接就空白了,本文章就是为了解决这个问题,如果底部留白那么就添加几条空数据就行了.如果数据已达到高度了那么就不会留白了 1.效果 这个空列可以根据高度来决定添加几个空格子去铺满列表&…...

vue实现无感刷新token

vue实现无感刷新token 1、前言2、实现思路2.1 方法一2.2 方法二2.3 方法三 3、可能遇到的问题3.1 问题一&#xff1a;如何防止多次刷新token3.2 问题二&#xff1a;同时发起两个或者两个以上的请求时&#xff0c;其他接口怎么解决 1、前言 最近在做vue3管理系统项目的时候&…...

竞赛选题 深度学习的动物识别

文章目录 0 前言1 背景2 算法原理2.1 动物识别方法概况2.2 常用的网络模型2.2.1 B-CNN2.2.2 SSD 3 SSD动物目标检测流程4 实现效果5 部分相关代码5.1 数据预处理5.2 构建卷积神经网络5.3 tensorflow计算图可视化5.4 网络模型训练5.5 对猫狗图像进行2分类 6 最后 0 前言 &#…...

龙虎榜——20250610

上证指数放量收阴线&#xff0c;个股多数下跌&#xff0c;盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型&#xff0c;指数短线有调整的需求&#xff0c;大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的&#xff1a;御银股份、雄帝科技 驱动…...

React 第五十五节 Router 中 useAsyncError的使用详解

前言 useAsyncError 是 React Router v6.4 引入的一个钩子&#xff0c;用于处理异步操作&#xff08;如数据加载&#xff09;中的错误。下面我将详细解释其用途并提供代码示例。 一、useAsyncError 用途 处理异步错误&#xff1a;捕获在 loader 或 action 中发生的异步错误替…...

使用分级同态加密防御梯度泄漏

抽象 联邦学习 &#xff08;FL&#xff09; 支持跨分布式客户端进行协作模型训练&#xff0c;而无需共享原始数据&#xff0c;这使其成为在互联和自动驾驶汽车 &#xff08;CAV&#xff09; 等领域保护隐私的机器学习的一种很有前途的方法。然而&#xff0c;最近的研究表明&…...

聊聊 Pulsar:Producer 源码解析

一、前言 Apache Pulsar 是一个企业级的开源分布式消息传递平台&#xff0c;以其高性能、可扩展性和存储计算分离架构在消息队列和流处理领域独树一帜。在 Pulsar 的核心架构中&#xff0c;Producer&#xff08;生产者&#xff09; 是连接客户端应用与消息队列的第一步。生产者…...

大数据零基础学习day1之环境准备和大数据初步理解

学习大数据会使用到多台Linux服务器。 一、环境准备 1、VMware 基于VMware构建Linux虚拟机 是大数据从业者或者IT从业者的必备技能之一也是成本低廉的方案 所以VMware虚拟机方案是必须要学习的。 &#xff08;1&#xff09;设置网关 打开VMware虚拟机&#xff0c;点击编辑…...

渲染学进阶内容——模型

最近在写模组的时候发现渲染器里面离不开模型的定义,在渲染的第二篇文章中简单的讲解了一下关于模型部分的内容,其实不管是方块还是方块实体,都离不开模型的内容 🧱 一、CubeListBuilder 功能解析 CubeListBuilder 是 Minecraft Java 版模型系统的核心构建器,用于动态创…...

postgresql|数据库|只读用户的创建和删除(备忘)

CREATE USER read_only WITH PASSWORD 密码 -- 连接到xxx数据库 \c xxx -- 授予对xxx数据库的只读权限 GRANT CONNECT ON DATABASE xxx TO read_only; GRANT USAGE ON SCHEMA public TO read_only; GRANT SELECT ON ALL TABLES IN SCHEMA public TO read_only; GRANT EXECUTE O…...

SpringBoot+uniapp 的 Champion 俱乐部微信小程序设计与实现,论文初版实现

摘要 本论文旨在设计并实现基于 SpringBoot 和 uniapp 的 Champion 俱乐部微信小程序&#xff0c;以满足俱乐部线上活动推广、会员管理、社交互动等需求。通过 SpringBoot 搭建后端服务&#xff0c;提供稳定高效的数据处理与业务逻辑支持&#xff1b;利用 uniapp 实现跨平台前…...

相机从app启动流程

一、流程框架图 二、具体流程分析 1、得到cameralist和对应的静态信息 目录如下: 重点代码分析: 启动相机前,先要通过getCameraIdList获取camera的个数以及id,然后可以通过getCameraCharacteristics获取对应id camera的capabilities(静态信息)进行一些openCamera前的…...

VTK如何让部分单位不可见

最近遇到一个需求&#xff0c;需要让一个vtkDataSet中的部分单元不可见&#xff0c;查阅了一些资料大概有以下几种方式 1.通过颜色映射表来进行&#xff0c;是最正规的做法 vtkNew<vtkLookupTable> lut; //值为0不显示&#xff0c;主要是最后一个参数&#xff0c;透明度…...