本文为原创文章,未获授权禁止转载,侵权必究!
本篇是 Vue3 源码解析系列第 6 篇,关注专栏
前言
当设计一个框架时,我们通常会选择 纯运行时
、纯编译时
、运行时 + 编译时
这三种。这里我们来聊聊 Vue3 源码中的 runtime
运行时,主要在 packages/runtime-core
目录下,核心提供了 h
、render
等函数。在讲解它们之前,我们需了解下 HTML DOM 树
和 虚拟 DOM
等概念。
HTML DOM 树
通过节点构成的一个树形结构,我们称为 HTML DOM
节点树。在 这篇文章 中做了详细的解释,这里就不再展开讲解,我们通过一个例子来一探究竟:
html
<div>
<h1>hello h1</h1>
<!-- 这里是注释 -->
hello div
</div>
浏览器看到上述代码时,它会生成一个对应的 DOM 树
来展示:
可以看出浏览器在运行代码时,无论是 标签
、文本
、注释
等都会生成对应的节点,这也就构成了 HTML DOM
树。
虚拟 DOM
什么是 虚拟 DOM
? Vue 文档 中也做了相应的解释,大家可以点击进行查看,大致我们可以理解为:
虚拟 DOM 是一种编程概念,意为将目标所需的 UI 通过数据结构 "虚拟" 地表示出来,保存在内存中,然后将真实的 DOM 与之保持同步。这个概念有 React 率先开拓,随后在许多不同的框架中都有不同的实现,当然也包括 Vue。
我们还是通过一个例子来说明下:
js
// 真实 DOM
<div>text</div>
// 虚拟 DOM 表示
const vnode = {
type: 'div', // 父节点类型 div
children: 'text' // 子节点内容 text
}
// 真实 DOM
<div>
<h1>hello h1</h1>
<!-- TODO: comment -->
hello div
</div>
// 虚拟 DOM 表示
const vnode = {
type: 'div', // 父节点类型 div
children: [ // 多个子节点
{
type: 'h1', // 子节点类型 h1
children: 'hello h1' // 子节点内容 hello h1
},
{
type: Comment, // 子节点类型 Comment
children: 'TODO: comment' // 子节点内容 TODO: comment
},
'hello div' // 子节点内容 hello div
]
}
可以看出 虚拟 DOM
类似把真实 DOM 转换成一个 VNode 对象
,该 对象
存在节点的类型 type
,子节点信息 children
等属性,而子节点有可能是 字符串
,也可能是包含其他子节点的 数组
。
而在 runtime
运行时,渲染器 renderer
会遍历整个 虚拟 DOM
树,并据此构建 真实的 DOM 树
,这个过程我们把它叫做挂载 mount
。
当这个 VNode
对象发生变化时,那么我们会对比 旧的 VNode
和 新的 VNode
之间的区别,找出它们之间的区别,并应用这其中的变化到真实的 DOM 上,这个过程被称为 更新 patch
。
Vue
中转换为 VNode
对象就是通过 h
函数来完成的,我们通过调试来看下 h
函数的返回值:
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<script src="../../../dist/vue.global.js"></script>
</head>
<body>
<div id="app"></div>
<script>
const { h } = Vue
const vnode = h(
'div',
{
class: 'test'
},
'hello render'
)
console.log(vnode)
</script>
</body>
</html>
js
// vnode
{
// 是否是一个 VNode 对象
"__v_isVNode": true,
// 当前节点类型
"type": "div",
// 当前节点属性
"props": { "class": "text" },
// 子节点
"children": "hello render"
}
h
函数定义在 packages/runtime-core/src/h.ts
文件中,它接收三个参数:
type: string | Component
既可以是一个字符串(用于原生元素)也可以是一个 Vue 组件定义props?: object | null
要传递的 propchildren?: Children | Slot | Slots
子节点
最终 VNode
对象通过 render
函数渲染为真实 DOM,该 render
函数定义在 packages/runtime-core/src/renderer.ts
文件中,实际执行的是 createRenderer
方法,它接收两个参数:
vnode
虚拟节点树 或者叫做虚拟 DOM 树container
承载的容器,真实节点渲染的位置
总结
- 理解
HTML DOM
树 和虚拟 DOM
的区别。 虚拟 DOM
实际是一个VNode
对象,包含节点类型、属性等信息。Vue3
中runtime
运行时,主要通过h
函数转换成VNode
对象,再通过render
函数渲染成真实 DOM 。