《Vue3 从入门到大神26篇》Virtual DOM 与 Diff 算法 —— Vue3 的高效更新机制 前言这一篇我们来了解vue3高效更新机制的核心逻辑很多人对 Virtual DOM虚拟 DOM有一个误解❌ “Virtual DOM 比原生 DOM 快。”这是一个伪命题。事实上操作 Virtual DOM 本身也有成本它之所以能提升性能是因为它能计算出最小更新量从而减少昂贵的 DOM 操作。而 Vue3 的 Diff 算法正是为了尽可能少地操作 DOM。这一篇我们系统讲解✅ 什么是 Virtual DOM✅ VNode 的设计✅ Vue3 的 Diff 策略✅ 最长递增子序列LIS在 Diff 中的作用✅ 为什么 Vue3 的 Diff 更快一、为什么需要 Virtual DOM1️⃣ 原生 DOM 的问题document.getElementById(app).innerHTML div p${count}/p buttonClick/button /div ❌ 问题DOM 操作非常昂贵重排、重绘每次更新都重新创建所有 DOM无法复用已有 DOM2️⃣ Virtual DOM 的核心思想数据变化 ↓ 生成新的 Virtual DOMJS 对象 ↓ Diff 对比新旧 VDOM ↓ 计算出最小变更 ↓ 只更新必要的真实 DOMVirtual DOM 是“中间层”不是“性能银弹”。二、VNodeVirtual DOM 的基本单元1️⃣ VNode 是什么interface VNode { type: string | object | null // 标签名 / 组件对象 props: Recordstring, any | null children: VNode[] | string | null el: HTMLElement | null // 对应的真实 DOM key?: string | number }2️⃣ 一个真实 VNode 示例const vnode { type: div, props: { class: container }, children: [ { type: p, props: null, children: Hello Vue3 } ], el: null }3️⃣ h 函数创建 VNode 的工具function h(type, props, children) { return { type, props, children, el: null } } // 使用 h(div, { class: container }, [ h(p, null, Hello Vue3) ])Vue 模板编译后最终都会变成h()调用。三、Diff 算法找出差异1️⃣ Diff 的本质给定两棵树旧 VDOM 和新 VDOM找出最小的修改操作。这是一个经典的树编辑距离问题理论上时间复杂度是O(n³)。Vue 做了一个重要假设只比较同一层级的节点不跨层级比较。这样时间复杂度降到了O(n)。2️⃣ Vue2 的双端 DiffVue2 使用的是双端比较算法旧[a, b, c, d] 新[b, c, e, d]比较流程旧头 vs 新头旧尾 vs 新尾旧头 vs 新尾旧尾 vs 新头✅ 优点逻辑直观❌ 缺点移动节点时不够高效四、Vue3 的 Diff更聪明的策略Vue3 在双端比较的基础上引入了最长递增子序列LIS优化。1️⃣ 核心思路1. 预处理处理相同的前缀和后缀 2. 构建 key - index 映射表 3. 找出最长不需要移动的节点序列 4. 只移动必要的节点2️⃣ 示例为什么要 LIS!-- 旧 VDOM -- ul li keyaA/li li keybB/li li keycC/li li keydD/li /ul !-- 新 VDOM -- ul li keybB/li li keycC/li li keyeE/li li keydD/li /ul变化A 被删除E 被插入B、C、D 顺序变化3️⃣ 最长递增子序列的作用Vue3 会找出在新旧 VDOM 中相对位置不变的节点序列旧索引 [0, 1, 2, 3] (a, b, c, d) 新索引 [1, 2, 3, 0] (b, c, d, a) 最长递增子序列 [1, 2, 3] → 对应 b, c, d结论b、c、d 这三个节点不需要移动只需要移动其他节点。五、LIS 算法简化实现function getLIS(arr) { const dp new Array(arr.length).fill(1) let maxLen 1 for (let i 1; i arr.length; i) { for (let j 0; j i; j) { if (arr[i] arr[j]) { dp[i] Math.max(dp[i], dp[j] 1) } } maxLen Math.max(maxLen, dp[i]) } // 回溯构造 LIS const lis [] for (let i arr.length - 1; i 0; i--) { if (dp[i] maxLen) { lis.unshift(i) maxLen-- } } return lis } // 示例 console.log(getLIS([1, 2, 3, 0])) // [0, 1, 2]Vue3 使用的是 O(n log n) 的贪心算法版本这里为了直观用了 O(n²)。六、Vue3 Diff 的核心优势对比项Vue2Vue3比较策略双端比较双端 LIS移动节点频繁移动最小化移动性能较好更好复杂度O(n)O(n)常数更小Vue3 的 Diff 不是更快而是更少移动 DOM。七、PatchFlags 与 Diff 的关系还记得第 22 篇讲的 PatchFlags 吗// 编译后的渲染函数 _createElementVNode(p, null, _toDisplayString(_ctx.count), 1 /* TEXT */)PatchFlags 是 Diff 的上游优化PatchFlags 告诉 Diff这个节点只有文本变化Diff 就只更新文本内容不再递归比较子节点✅两者结合效果翻倍。八、手写一个简化的 Diff理解版function diff(oldChildren, newChildren, container) { const oldKeyToIndex {} oldChildren.forEach((child, i) { oldKeyToIndex[child.key] i }) const newIndices new Array(newChildren.length).fill(-1) // 建立新节点到旧节点的映射 newChildren.forEach((child, i) { const oldIndex oldKeyToIndex[child.key] if (oldIndex ! undefined) { newIndices[i] oldIndex } }) // 计算 LIS const lis getLIS(newIndices) // 逆序遍历移动节点 let j lis.length - 1 for (let i newChildren.length - 1; i 0; i--) { const newChild newChildren[i] const oldIndex newIndices[i] if (oldIndex -1) { // 新增节点 mount(newChild, container) } else { if (i ! lis[j]) { // 需要移动 move(newChild, container) } else { // 不需要移动 j-- } } } }九、面试高频问答Q1Virtual DOM 一定比原生 DOM 快吗不一定。VDOM 的优势在于减少 DOM 操作次数而不是操作本身更快。Q2Vue3 的 Diff 和 Vue2 有什么不同Vue3 在双端比较基础上引入 LIS最小化节点移动次数。Q3LIS 在 Diff 中的作用是什么找出不需要移动的节点序列只移动必要的节点。十、总结原理级Virtual DOM 是描述 UI 的 JS 对象Diff 的目标是最小化 DOM 操作Vue3 的 Diff 双端比较 LISPatchFlags 是 Diff 的上游优化少移动 DOM才是真正的快 下期预告第 27 篇编译时优化揭秘 —— Vue 模板是如何变成 JS 代码的

相关新闻

最新新闻

ReactOS 0.3.15 源码编译实战:从环境准备到虚拟机启动

ReactOS 0.3.15 源码编译实战:从环境准备到虚拟机启动

简介:ReactOS-0.3.15-REL-src.zip是一份基于ReactOS 0.3.15版的完整源代码压缩包,面向对Windows内核机制感兴趣的系统开发者、驱动研究者和开源操作系统学习者。ReactOS以兼容Windows应用与驱动为目标,此版本源码可用于剖析内核对象管理、进程…

2026/9/9 9:46:37
platform-tools.zip 报错无法定位程序输入点?adb 与 fastboot 配置实战指南

platform-tools.zip 报错无法定位程序输入点?adb 与 fastboot 配置实战指南

简介:这份工具包是面向安卓开发与测试人员的平台工具集合,重点解决调试过程中常见的 ADB 服务版本与客户端版本不一致、找不到设备或模拟器等问题。压缩包体积仅 1.74MB,共包含 22 个文件,其中既有安卓调试桥、fastboot、SQLite 命…

2026/9/9 9:46:37
功能测试转质量工程师:六步转型方法论

功能测试转质量工程师:六步转型方法论

做了三年功能测试,每天不是点点点就是写用例、提bug、回归验证,手上活越来越熟练,心里却越来越不踏实——这是去年一位读者跟我聊的第一句话。功能测试是很多人的第一份测试岗位,但越来越多的人发现,再往下走&#xff…

2026/9/9 9:46:37
ARM+RTOS+机器人:2026高薪嵌入式工程师的底层能力图谱

ARM+RTOS+机器人:2026高薪嵌入式工程师的底层能力图谱

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/9 9:46:37
PostIn:开源轻量级接口管理工具,能否替代Postman?

PostIn:开源轻量级接口管理工具,能否替代Postman?

说实话,看到“超轻量”三个字,我一开始是不信的。这几年“轻量级”三个字被各种工具用烂了,打开一看不是 Electron 套壳就是阉割到没法用的“玩具”。但 PostIn 这个开源接口管理工具我试了两天之后,确实把 Postman 从我的主力调试…

2026/9/9 9:46:37
C语言入门到实战:从环境搭建、指针内存到项目开发完整路线

C语言入门到实战:从环境搭建、指针内存到项目开发完整路线

先说实话:C语言这门课,几乎每个学编程的人都会经历一遍“从收藏一堆教程到不知道从哪下手”的过程。你在搜索引擎里输入“C语言基础知识”“C语言入门教程免费”“翁恺C语言练习题”,翻到的资料足够读好几年,但真正让你从零到能独…

2026/9/9 9:41:37