【Vue原理解析】之虚拟DOM

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

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

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

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

functionpatchVnode(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);
    } elseif (isDef(ch)) {
// 添加新的子节点// ...addVnodes(elm, null, ch, 0, ch.length-1, insertedVnodeQueue);
    } elseif (isDef(oldCh)) {
// 移除旧的子节点removeVnodes(elm, oldCh, 0, oldCh.length-1);
    }
  } elseif (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"`
  1. 将差异添加到补丁数组中。
    补丁数组: [{ type: 'props', prop: 'id', value: 'new' }]
  2. 比较子节点。子节点不同:
  • 旧VNode有一个文本节点:"Hello, Vue!"
  • 新VNode有一个文本节点:"Hello, Vue.js!",以及一个子节点<span>Extra content</span>
  1. 将差异添加到补丁数组中。
    补丁数组:
[
    { 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应用程序。

目录
相关文章
|
9月前
|
人工智能 JavaScript 算法
Vue 中 key 属性的深入解析:改变 key 导致组件销毁与重建
Vue 中 key 属性的深入解析:改变 key 导致组件销毁与重建
1017 0
|
10月前
|
JavaScript 数据可视化 前端开发
基于 Vue 与 D3 的可拖拽拓扑图技术方案及应用案例解析
本文介绍了基于Vue和D3实现可拖拽拓扑图的技术方案与应用实例。通过Vue构建用户界面和交互逻辑,结合D3强大的数据可视化能力,实现了力导向布局、节点拖拽、交互事件等功能。文章详细讲解了数据模型设计、拖拽功能实现、组件封装及高级扩展(如节点类型定制、连接样式优化等),并提供了性能优化方案以应对大数据量场景。最终,展示了基础网络拓扑、实时更新拓扑等应用实例,为开发者提供了一套完整的实现思路和实践经验。
1364 78
|
7月前
|
缓存 前端开发 大数据
虚拟列表在Vue3中的具体应用场景有哪些?
虚拟列表在 Vue3 中通过仅渲染可视区域内容,显著提升大数据列表性能,适用于 ERP 表格、聊天界面、社交媒体、阅读器、日历及树形结构等场景,结合 `vue-virtual-scroller` 等工具可实现高效滚动与交互体验。
732 1
|
10月前
|
机器学习/深度学习 数据采集 JavaScript
用深度学习提升DOM解析——自动提取页面关键区块
本文介绍了一次二手车数据爬虫事故的解决过程,从传统XPath方案失效到结合深度学习语义提取的成功实践。面对懂车帝平台的前端异步渲染和复杂DOM结构,通过Playwright动态渲染、代理IP隐藏身份,以及BERT模型对HTML块级语义识别,实现了稳定高效的字段提取。此方法抗结构变化能力强,适用于复杂网页数据采集,如二手车、新闻等领域。架构演进从静态爬虫到动态爬虫再到语义解析,显著提升效率与稳定性。
361 13
用深度学习提升DOM解析——自动提取页面关键区块
|
9月前
|
JavaScript 前端开发 UED
Vue 手风琴实现的三种常用方式及长尾关键词解析
手风琴效果是Vue开发中常见的交互组件,可节省页面空间、提升用户体验。本文介绍三种实现方式:1) 原生Vue结合数据绑定与CSS动画;2) 使用Element UI等组件库快速构建;3) 自定义指令操作DOM实现独特效果。每种方式适用于不同场景,可根据项目需求选择。示例包括产品特性页、后台菜单及FAQ展示,灵活满足多样需求。附代码示例与资源链接,助你高效实现手风琴功能。
500 10
|
9月前
|
JavaScript 前端开发 UED
Vue 表情包输入组件实现代码及详细开发流程解析
这是一篇关于 Vue 表情包输入组件的使用方法与封装指南的文章。通过安装依赖、全局注册和局部使用,可以快速集成表情包功能到 Vue 项目中。文章还详细介绍了组件的封装实现、高级配置(如自定义表情列表、主题定制、动画效果和懒加载)以及完整集成示例。开发者可根据需求扩展功能,例如 GIF 搜索或自定义表情上传,提升用户体验。资源链接提供进一步学习材料。
551 1
|
JavaScript 算法
Vue 中如何手动更新虚拟 DOM?
Vue 中如何手动更新虚拟 DOM?
590 57
|
机器学习/深度学习 数据可视化 PyTorch
深入解析图神经网络注意力机制:数学原理与可视化实现
本文深入解析了图神经网络(GNNs)中自注意力机制的内部运作原理,通过可视化和数学推导揭示其工作机制。文章采用“位置-转移图”概念框架,并使用NumPy实现代码示例,逐步拆解自注意力层的计算过程。文中详细展示了从节点特征矩阵、邻接矩阵到生成注意力权重的具体步骤,并通过四个类(GAL1至GAL4)模拟了整个计算流程。最终,结合实际PyTorch Geometric库中的代码,对比分析了核心逻辑,为理解GNN自注意力机制提供了清晰的学习路径。
782 7
深入解析图神经网络注意力机制:数学原理与可视化实现
|
12月前
|
传感器 人工智能 监控
反向寻车系统怎么做?基本原理与系统组成解析
本文通过反向寻车系统的核心组成部分与技术分析,阐述反向寻车系统的工作原理,适用于适用于商场停车场、医院停车场及火车站停车场等。如需获取智慧停车场反向寻车技术方案前往文章最下方获取,如有项目合作及技术交流欢迎私信作者。
942 2

推荐镜像

更多
  • DNS