runtime
运行时,主要在packages/runtime-core
目录下,核心提供了h
、render
等函数。在理解它们之前,我们需要了解下HTML DOM 树
和虚拟 DOM
等概念
HTML DOM 树
通过节点构成的一个树形结构,我们称为HTML DOM
节点树。DOM 文档里面做了详细解释,这里就不再展开讲解,通过以下例子来简单理解:
<div> <h1>hello h1</h1> <!-- 这里是注释 --> hello div
</div>
浏览器运行上述代码时,它会生成一个对应的DOM 树
来展示:
虚拟DOM
虚拟 DOM 是一种编程概念,意为将目标所需的 UI 通过数据结构“虚拟”地表示出来,保存在内存中,然后将真实的 DOM 与之保持同步,详细解释可见 Vue文档
通过以下这个例子来说明:
可以看出虚拟 DOM 类似把真实 DOM 转换成一个 VNode 对象,该对象存在节点的类型type
、子节点信息children
等属性,而子节点有可能是字符串,也可能是包含其他子节点的数组
// 真实 DOM
<div>text</div>// 虚拟 DOM 表示
const vnode = {type: 'div', // 父节点类型 divchildren: 'text' // 子节点内容 text
}// 真实 DOM
<div><h1>hello h1</h1><!-- TODO: comment -->hello div
</div>// 虚拟 DOM 表示
const vnode = {type: 'div', // 父节点类型 divchildren: [ // 多个子节点{type: 'h1', // 子节点类型 h1children: 'hello h1' // 子节点内容 hello h1 },{type: Comment, // 子节点类型 Commentchildren: 'TODO: comment' // 子节点内容 TODO: comment },'hello div' // 子节点内容 hello div]
}
而在runtime
运行时,渲染器render
会遍历整个虚拟 DOM 树,并据此构建真实的 DOM 树,这个过程把它叫做挂载mount
当这个VNode
对象发生变化时,那么我们会对比旧的 VNode
和新的 VNode
之间的区别,找出它们之间的区别,并应用这其中的变化到真实的 DOM 上,这个过程被称为更新patch
h
Vue
中转换为VNode
对象就是通过h
函数来完成的,通过调试来看下h
函数的返回值:
<!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 } = Vuedebuggerconst vnode = h('div',{class: 'test'},'hello render')console.log(vnode)</script></body>
</html>
h
函数定义在packages/runtime-core/src/h.ts
文件中,它接收三个参数:
type: string | Component
:既可以是一个字符串(用于原生元素),也可以是一个 Vue 组件定义props?: object | null
:要传递的 propchildren?: Children | Slot | Slots
:子节点
该 vnode 如下:
最终VNode
对象通过render
函数渲染为真实 DOM,该render
函数定义在packages/runtime-core/src/renderer.ts
文件中,实际执行的是createRenderer
方法,它接收两个参数:
vnode
虚拟节点树,或者叫做虚拟 DOM 树container
承载的容器,真实节点渲染的位置