【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应用程序。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

暖手宝上架亚马逊美国站UL499报告测试标准要求

暖手宝是运用物理及化学原理研制的自动取暖保健用品。该产品以其自动生热&#xff0c;有趣&#xff0c;实用等新颖独特的优势&#xff0c;深受欢迎——暖手宝具有自动取暖&#xff0c;理疗保健等多种功能。只要插上电源等上10分钟左右就能发热&#xff0c;最后一种是通过锂电池…

【华为OD题库-015】报文重排序-Java

题目 对报文进行重传和重排序是常用的可靠性机制&#xff0c;重传缓冲区内有一定数量的子报文&#xff0c;每个子报文在原始报文中的顺序已知&#xff0c;现在需要恢复出原始报文。 输入描述 输入第一行为N,表示子报文的个数&#xff0c;0<N < 1000。 输入第二行为N个子报…

多线程 浏览器渲染引擎 图形用户界面(GUI,Graphical User Interface)应用程序

目录 多线程浏览器渲染引擎图形用户界面&#xff08;GUI&#xff0c;Graphical User Interface&#xff09;应用程序 &#x1f44d; 点赞&#xff0c;你的认可是我创作的动力&#xff01; ⭐️ 收藏&#xff0c;你的青睐是我努力的方向&#xff01; ✏️ 评论&#xff0c;你的…

【FISCO BCOS】十九、区块链浏览器部署

目录 一、环境依赖 检查环境 1.检查java 二、拉取安装脚本 获取部署安装包 ​编辑 解压安装包 进入目录 三、修改配置 四、部署服务 五、状态检查 检查前后端进程 1.检查后端server进程 2.检查前端的nginx进程 检查进程端口 六、使用区块链浏览器 1.配置群组…

EDA实验-----3-8译码器设计(QuartusII)

目录 一. 实验目的 二. 实验仪器 三. 实验原理及内容 1.实验原理 2.实验内容 四&#xff0e;实验步骤 五. 实验报告 六. 注意事项 七. 实验过程 1.创建Verilog文件&#xff0c;写代码 ​编辑 2.波形仿真 3.连接电路图 4.烧录操作 一. 实验目的 学会Verilog HDL的…

【Java 进阶篇】Java与JQuery:探秘事件绑定、入口函数与样式控制

在现代的Web开发中&#xff0c;Java和JQuery是两个不可或缺的角色。Java为我们提供了强大的后端支持&#xff0c;而JQuery则是前端开发的得力助手。本篇博客将围绕Java和JQuery&#xff0c;深入探讨事件绑定、入口函数和样式控制&#xff0c;带你进入前端开发的奇妙世界。 Jav…

ROS基础—vscode创建工作空间

1、创建ROS工作空间 首先打开ubuntu的终端&#xff0c;接着依次输入如下的命令行&#xff1b; mkdir -p xxx_ws/src(必须得有 src) cd xxx_ws catkin_make当然我一般是新建一个叫做demo的工作空间&#xff0c;如 mkdir -p demo04_ws/src 2、启动vscode cd xxx_ws code . …

粉够荣获淘宝联盟区域理事会常务理事,携手共铸淘客新生态

淘宝联盟区域理事会于2021年成立&#xff0c;首届成立成都、广州、武汉&#xff0c;服务近2000个领军淘宝客企业&#xff0c;作为区域生态与官方交流重要枢纽&#xff0c;理事会举办近百场交流分享会&#xff0c;带动淘客跨域跨业态交流成长。 2023年9月7日第二届淘宝联盟理事…

Unity中关于Lerp()方法的使用

在Unity中&#xff0c;Lerp()方法用于在两个值之间进行线性插值。 它的语法有&#xff1a; public static float Lerp(float a, float b, float t);//在两个float类型的值a和b之间进行线性插值 public static Vector2 Lerp(Vector2 a, Vector2 b, float t);//在两个Vector2类…

【Maven教程】(十):使用 Hudson 进行持续集成—— 从Hudson的安装到任务创建 ~

Maven 使用 Hudson 进行持续集成 1️⃣ 持续集成的作用、过程和优势2️⃣ Hudson 简介与安装3️⃣ 准备 Subversion 仓库4️⃣ Hudson 的基本系统设置5️⃣ 创建 Hudson 任务5.1 Hudson 任务的基本配置5.2 Hudson 任务的源码仓库配置5.3 Hudson 任务的构建触发配置5.4 Hudson …

debian 添加开机启动项

有的时候&#xff0c;经常有自己需要的程序&#xff0c;官方并没有提供添加开机启动的方法&#xff0c;但我们却需要让他们开机启动&#xff08;比如探针类、飞机类、服务类程序等&#xff09;。可能有的人会说&#xff0c;可以使用 /etc/rc.local 啊。是可以使用这个&#xff…

AI:86-基于深度学习的人体姿态估计与运动分析

🚀 本文选自专栏:人工智能领域200例教程专栏 从基础到实践,深入学习。无论你是初学者还是经验丰富的老手,对于本专栏案例和项目实践都有参考学习意义。 ✨✨✨ 每一个案例都附带有在本地跑过的代码,详细讲解供大家学习,希望可以帮到大家。欢迎订阅支持,正在不断更新中,…

Leetcode—2469.温度转换【简单】

2023每日刷题&#xff08;二十六&#xff09; Leetcode—2469.温度转换 实现代码 /*** Note: The returned array must be malloced, assume caller calls free().*/ double* convertTemperature(double celsius, int* returnSize) {double* ans (double *)malloc(sizeof(do…

Springboot+vue的高校办公室行政事务管理系统(有报告)。Javaee项目,springboot vue前后端分离项目。

演示视频&#xff1a; Springbootvue的高校办公室行政事务管理系统&#xff08;有报告&#xff09;。Javaee项目&#xff0c;springboot vue前后端分离项目 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。…

某XX自考小程序的AES加密分析

前言 主要是报了自考在这个小程序上面做题&#xff0c;就研究了一下这个接口本文仅供学习交流使用&#xff0c;请勿随意传播。如有侵犯你的权益及时联系我删除。 一、抓包分析打开小程序&#xff0c;打开devtools 工具&#xff0c;这里就不啰嗦&#xff0c;直接上过程。 点击…

入选《人工智能领域内容榜》

2023-11-13 入选《人工智能领域内容榜》31 C# OpenCvSharp DNN HybridNets 同时处理车辆检测、可驾驶区域分割、车道线分割

maven 私有仓库配置

1.整体库信息 2.配置阿里云库 &#xff08;可以配置多个库&#xff0c;再引用代理库&#xff09; 3.建立自己的 发布&#xff0c;快照库 4.建立自由的公共库- 引用所有需要的库 5.maven setting 中配置 用户名密码 <server><id>mv-releases</id><usernam…

Java_常用API

API(全称Application Programming Interface:应用程序编程接口) String 常用方法 注意事项 每一次试图改变字符串对象都产生了新的字符串对象 ArrayList 常用方法 ATM系统 01系统架构搭建、欢迎页设计 02开户功能实现 03登录功能实现 04操作页展示、查询账户、退出账户 05存…

输入一个url后,会发生什么事?

Internet上的每一个网页都具有一个唯一的名称标识&#xff0c;通常称之为URL&#xff08;Uniform Resource Locator,统一资源定位器&#xff09;。它是www的统一资源定位标志&#xff0c;简单地说URL就是web地址&#xff0c;俗称“网址”。 所以当我们在浏览器上输入一个url后&…