虚拟 DOM 实现是 Vue 框架的核心部分之一,它负责在真实 DOM 之上抽象出一个轻量级的、可复用的 JavaScript 对象树,用于高效地更新视图。
什么是虚拟DOM?
虚拟 DOM 是一个编程概念,它将真实的 DOM 树抽象为一个轻量级的 JavaScript 对象树。当应用状态发生变化时,Vue 会先比较新的虚拟 DOM 树和旧的虚拟 DOM 树之间的差异,然后只更新这些差异部分对应的真实 DOM,而不是重新渲染整个页面。这种方式大大提高了渲染性能。
而我们如何将虚拟DOM转化成真实DOM呢?
App.vue:
<template>
<div>
<a href="https://vitejs.dev" target="_blank">
<img src="/vite.svg" class="logo" alt="Vite logo" />
</a>
<a href="https://vuejs.org/" target="_blank">
<img src="./assets/vue.svg" class="logo vue" alt="Vue logo" />
</a>
</div>
<HelloWorld msg="Vite + Vue" />
</template>
从我们编写的vue代码以及转换成真实DOM整个流程如下:
模板 > render函数 > 虚拟DOM > 真实DOM
上图中是我写的App.vue。我们将其打印出来
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
console.log(App); // 打印出来的模板
createApp(App).mount('#app')
打印结果:
我们可以看到这个render函数,我们的虚拟DOM是由render函数创建的。
export function render(_ctx, _cache, $props, $setup, $data, $options) {
const _component_HelloWorld = _resolveComponent("HelloWorld")
return (_openBlock(), _createElementBlock("template", null, [
_createElementVNode("div", null, [
_createElementVNode("a", {
href: "https://vitejs.dev",
target: "_blank"
}, [
_createElementVNode("img", {
src: "/vite.svg",
class: "logo",
alt: "Vite logo"
})
]),
_createElementVNode("a", {
href: "https://vuejs.org/",
target: "_blank"
}, [
_createElementVNode("img", {
src: "./assets/vue.svg",
class: "logo vue",
alt: "Vue logo"
})
])
]),
_createVNode(_component_HelloWorld, { msg: "Vite + Vue" })
]))
}
当我的虚拟DOM创建出来后,如何转成真实DOM呢?
这里用到了patch 函数:
patch 是 Vue 中用于比较和更新虚拟 DOM 的核心函数。它接受两个参数:旧的 VNode 和新的 VNode。根据这两个节点的类型和属性等信息,patch 函数会决定是否需要更新真实 DOM,以及如何更新。
简单表示下patch相关作用!
function patch(
n1: VNode | null, // 旧虚拟DOM
n2: VNode, // 新的虚拟DOM
container: HostNode,
anchor: ?HostNode = null,
parentComponent: ?Component = null,
parentSuspense: ?SuspenseBoundary = null,
isSVG: boolean = false,
optimized: boolean = false
): VNode {
// ...
const { type, ref, shapeFlag } = n2;
switch (type) {
case Text:
// 处理文本节点
// ...
break;
case Comment:
// 处理注释节点
// ...
break;
case Static:
// 处理静态节点
// ...
break;
case Fragment:
// 处理 Fragment 节点
// ...
break;
default:
// 处理元素或组件节点
if (shapeFlag & ShapeFlags.ELEMENT) {
// ... 处理元素节点 ...
} else if (shapeFlag & ShapeFlags.COMPONENT) {
// ... 处理组件节点 ...
}
// ...
}
// ... 其他逻辑,如处理子节点、引用、挂载等 ...
}
后面单开一章讲解patch!