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

手写 Vue 中虚拟 DOM 到真实 DOM 的完整过程

目录

一、虚拟 DOM 的核心概念

二、虚拟 DOM 到真实 DOM 的流程

三、手写虚拟 DOM 到真实 DOM 的实现

1. 定义虚拟 DOM 的结构(VNode) 

 2. 创建虚拟 DOM 转真实 DOM 的函数

 3. 挂载虚拟 DOM 到页面

 4. 更新虚拟 DOM 的过程(Diff 算法简化版)

四、完整示例:虚拟 DOM 到真实 DOM 的生命周期

五、总结


一、虚拟 DOM 的核心概念

虚拟 DOM 是用 JavaScript 对象(VNode)模拟真实 DOM 结构的轻量级抽象。它的核心作用是:

  1. 减少直接操作真实 DOM 的次数:通过对比新旧虚拟 DOM 树的差异(Diff 算法),仅更新变化的部分。
  2. 声明式编程:开发者只需关注数据逻辑,无需手动操作 DOM。
  3. 跨平台能力:虚拟 DOM 可用于 Web、移动端(如 Weex)或服务端渲染(SSR)

 

二、虚拟 DOM 到真实 DOM 的流程

Vue 的渲染流程可分为以下步骤:

  1. 模板编译:将模板(<template>)编译为渲染函数(render function)。
  2. 生成虚拟 DOM 树:通过 render 函数返回一个虚拟 DOM 树(由 VNode 节点组成)。
  3. 首次挂载:将虚拟 DOM 树转化为真实 DOM 并渲染到页面。
  4. 数据更新:生成新的虚拟 DOM 树,与旧树进行 Diff 算法比较。
  5. 局部更新:将差异部分应用到真实 DOM 上(Patch 过程)。

三、手写虚拟 DOM 到真实 DOM 的实现

以下是一个简化版的实现示例,涵盖虚拟 DOM 的创建、挂载和更新过程。

1. 定义虚拟 DOM 的结构(VNode) 
// VNode 结构
const vnode = {tag: 'div',          // 标签名props: { id: 'app' }, // 属性children: [          // 子节点{tag: 'p',props: { class: 'text' },children: ['Hello, Vue!']}]
};
 2. 创建虚拟 DOM 转真实 DOM 的函数
// 将虚拟 DOM 转换为真实 DOM
function createDom(vnode) {const { tag, props, children } = vnode;const dom = document.createElement(tag); // 创建真实 DOM 元素// 设置属性if (props && typeof props === 'object') {updateProps(dom, props);}// 处理子节点if (Array.isArray(children)) {reconcileChildren(children, dom);}return dom;
}// 设置属性
function updateProps(dom, props) {for (const key in props) {if (key === 'style') {// 处理 style 属性for (const styleKey in props.style) {dom.style[styleKey] = props.style[styleKey];}} else {// 处理其他属性(id、class 等)dom[key] = props[key];}}
}// 递归处理子节点
function reconcileChildren(children, dom) {for (const child of children) {const childDom = createDom(child); // 递归创建子节点dom.appendChild(childDom);}
}

 

 3. 挂载虚拟 DOM 到页面
// 将虚拟 DOM 挂载到容器
function render(vnode, container) {const dom = createDom(vnode); // 生成真实 DOMcontainer.appendChild(dom);   // 挂载到页面
}// 示例:将虚拟 DOM 挂载到 #root 容器
const root = document.getElementById('root');
render(vnode, root);
 4. 更新虚拟 DOM 的过程(Diff 算法简化版)
// 比较新旧虚拟 DOM 树并更新真实 DOM
function patch(oldVnode, newVnode, parentDom) {// 如果节点类型不同,直接替换if (oldVnode.tag !== newVnode.tag) {parentDom.replaceChild(createDom(newVnode), oldVnode.elm);return;}// 获取真实 DOMconst elm = (newVnode.elm = oldVnode.elm);// 更新属性updateProps(elm, oldVnode.props, newVnode.props);// 递归比较子节点patchChildren(oldVnode.children, newVnode.children, elm);
}// 更新属性
function updateProps(dom, oldProps, newProps) {// 移除旧属性for (const key in oldProps) {if (!newProps[key]) {dom[key] = null;}}// 更新或新增属性for (const key in newProps) {if (key === 'style') {for (const styleKey in newProps.style) {dom.style[styleKey] = newProps.style[styleKey];}} else {dom[key] = newProps[key];}}
}// 递归比较子节点
function patchChildren(oldChildren, newChildren, parentDom) {// 简化版:逐个比较子节点const maxLength = Math.max(oldChildren.length, newChildren.length);for (let i = 0; i < maxLength; i++) {const oldChild = oldChildren[i];const newChild = newChildren[i];if (oldChild && newChild) {patch(oldChild, newChild, parentDom);} else if (newChild) {// 新增节点parentDom.appendChild(createDom(newChild));} else if (oldChild) {// 删除节点parentDom.removeChild(oldChild.elm);}}
}

四、完整示例:虚拟 DOM 到真实 DOM 的生命周期

<!DOCTYPE html>
<html>
<head><meta charset="UTF-8"><title>手写 Vue 虚拟 DOM</title>
</head>
<body><div id="root"></div><script>// 1. 初始虚拟 DOMconst initialVnode = {tag: 'div',props: { id: 'app' },children: [{tag: 'p',props: { class: 'text' },children: ['Hello, Vue!']}]};// 2. 挂载初始虚拟 DOMconst root = document.getElementById('root');const initialDom = createDom(initialVnode);root.appendChild(initialDom);// 3. 模拟数据更新后的新虚拟 DOMconst newVnode = {tag: 'div',props: { id: 'app' },children: [{tag: 'p',props: { class: 'text updated' },children: ['Hello, Vue! Updated!']}]};// 4. 更新虚拟 DOM 到真实 DOMpatch(initialVnode, newVnode, root);</script>
</body>
</html>

五、总结

通过上述实现,我们可以看到 Vue 虚拟 DOM 的核心原理:

  1. 虚拟 DOM 是 JavaScript 对象:通过 createDom 函数将虚拟 DOM 转化为真实 DOM。
  2. Diff 算法是性能优化的关键:通过比较新旧虚拟 DOM 树的差异,仅更新变化的部分。
  3. 局部更新减少重排/重绘成本:避免了直接操作真实 DOM 的高昂代价。

在实际开发中,Vue 的虚拟 DOM 实现更为复杂(如处理组件、事件绑定等),但核心思想一致。掌握虚拟 DOM 的原理,不仅能帮助我们更好地理解 Vue 的运行机制,还能在性能优化和跨平台开发中游刃有余。

 

http://www.lryc.cn/news/581395.html

相关文章:

  • .NET9 实现排序算法(MergeSortTest 和 QuickSortTest)性能测试
  • LinkedList 链表数据结构实现 (OPENPPP2)
  • 前端面试专栏-算法篇:18. 查找算法(二分查找、哈希查找)
  • AI智能体革命:从对话机器到自主决策的进化之路 **——当AI长出“手和脑”,一场人机协作范式转移正在发生
  • AI小智项目全解析:软硬件架构与开发环境配置
  • 图灵完备之路(数电学习三分钟)----解码器
  • Pytest 测试发现机制详解:自动识别测试函数与模块
  • 理想汽车6月交付36279辆 第二季度共交付111074辆
  • 比较两个csv文件的内容是否一致
  • Python 机器学习核心入门与实战进阶 Day 3 - 决策树 随机森林模型实战
  • HTML初学者第三天
  • centos 7.6安装mysql8
  • 基于大模型的肾积水全周期预测与诊疗方案研究报告
  • 03每日简报20250705
  • Qt开发:QListWidget的介绍和使用
  • java整合itext pdf实现自定义PDF文件格式导出
  • 画笔的进化:生成式AI与艺术创造力的范式革命
  • 完成ssl不安全警告
  • 数据结构:数组:二分查找(Binary Search)
  • 用 Turbo Vision 2 为 Qt 6 控制台应用创建 TUI 字符 MainFrame
  • Java-继承
  • 隐马尔可夫模型:语音识别系统的时序解码引擎
  • nginx部署发布Vite项目
  • 苹果系统查看已连接Wi-Fi密码的实用指南
  • 408第三季part2 - 计算机网络 - 物理层
  • HarmonyOS学习2---工程目录UIAbility
  • 零基础保姆级本地化部署文心大模型4.5开源系列
  • c++文字游戏_闯关打怪
  • stm32的三种开发方式
  • 【论文解读】Referring Camouflaged Object Detection