可以看到,在编译器的内部,实际上又分为了:
- 解析器:负责将模板解析为对应的模板 AST(抽象语法树)
- 转换器:负责将模板 AST 转换为 JS AST
- 生成器:负责将 JS AST 生成对应的 JS 代码(渲染函数)
约 977 字大约 3 分钟
2026-03-01
介绍一下 Vue3 内部的运行机制是怎样的
Vue3 整体可以分为:响应式系统、编译器和渲染器三大部分
使用 JS 描述一个 UI 通常可以分为:DOM 元素、属性、事件、元素的层次结构来进行描述
// <h1 id='title' @click=handler><span>hello</span></h1>
const obj = {
tag: 'h1',
props: {
id: 'title',
onClick: handler
},
children: [
{
tag: 'span',
children: 'hello'
}
]
}虽然这种方式能够描述出来 UI,但是非常麻烦,因此 Vue 提供了模板的方式。用户可以直接通过书写模板来进行 UI 的描述,然后通过编译器将模板编译为渲染函数,渲染函数执行后得到上面的 JS 对象也就是虚拟 DOM
主要负责将开发者所写的 模板转换为渲染函数,执行渲染函数,就会得到 JS 对象形式的 UI 表达
<template>
<div>
<h1 :id="someId">Hello</h1>
</div>
</template>// 编译后的结果
function render() {
return h('div', [h('h1', { id: someId }, 'Hello')])
}整个编译过程如下图所示:

可以看到,在编译器的内部,实际上又分为了:
执行渲染函数得到虚拟 DOM,即描述 UI 的 JS 对象
<div>点击</div>const vnode = {
tag: 'div',
props: {
onClick: () => alert('hello')
},
children: '点击'
}渲染器拿到这个虚拟 DOM 后,就会将其转换为真实的 DOM

基础:挂载普通元素
当 vnode.tag 为字符串时,描述的是普通 HTML 元素,渲染逻辑可分为三步:创建元素 → 添加属性与事件 → 处理 children(递归挂载)
扩展:组件
实际应用中,vnode.tag 还可能是 组件。组件本质是一组 DOM 的封装,可抽象为 "能返回 vnode 的函数或对象"——渲染组件时,只需调用它得到子 vnode,再对子 vnode 递归渲染即可。因此 tag 不再局限于 HTML 标签名
| tag 类型 | 含义 | 处理方式 |
|---|---|---|
| string | HTML 元素 | mountElement:创建 DOM、挂载属性/事件、递归 children |
| function | 函数组件 | mountComponent:执行函数得到子 vnode,再递归 renderer |
| object | 对象组件 | mountComponent:执行 render 方法得到子 vnode,再递归 renderer |
完整实现
const MyComponent = function () {
return {
tag: 'div',
props: { onClick: () => alert('hello') },
children: 'click me'
}
}
const vnode = { tag: MyComponent }const MyComponent = {
render() {
return {
tag: 'div',
props: { onClick: () => alert('hello') },
children: 'click me'
}
}
}
const vnode = { tag: MyComponent }function renderer(vnode, container) {
if (typeof vnode.tag === 'string') {
mountElement(vnode, container)
} else if (typeof vnode.tag === 'function' || typeof vnode.tag === 'object') {
mountComponent(vnode, container)
}
}
function mountElement(vnode, container) {
const el = document.createElement(vnode.tag)
for (const key in vnode.props || {}) {
if (/^on/.test(key)) {
el.addEventListener(key.slice(2).toLowerCase(), vnode.props[key])
}
}
if (typeof vnode.children === 'string') {
el.appendChild(document.createTextNode(vnode.children))
} else if (Array.isArray(vnode.children)) {
vnode.children.forEach((child) => renderer(child, el))
}
container.appendChild(el)
}
function mountComponent(vnode, container) {
const subtree = typeof vnode.tag === 'function' ? vnode.tag() : vnode.tag.render()
renderer(subtree, container)
}重要
当模板编译成的渲染函数执行时,渲染函数内部用到的响应式数据会和渲染函数本身构成依赖关系,之后只要响应式数据发生变化,渲染函数就会重新执行,渲染函数重新执行后得到新的虚拟 DOM,然后渲染器会使用新的虚拟 DOM 与旧的虚拟 DOM 进行对比,找出差异,然后只更新差异部分的真实 DOM,从而实现视图的更新