Vue.js 源码揭秘(一):Vue3 架构总览
Vue.js 源码揭秘(一):Vue3 架构总览
本文从全局视角解析 Vue3 的核心架构,建立源码阅读的整体认知。
一、整体架构
┌─────────────────────────────────────────────────────────────┐
│ Vue Application │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ Compiler (编译时) │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Parse │─►│ Transform │─►│ Codegen │ │
│ │ (解析) │ │ (转换) │ │ (代码生成) │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
└─────────────────────────────────────────────────────────────┘
│
▼ render function
┌─────────────────────────────────────────────────────────────┐
│ Runtime (运行时) │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Reactivity │ │ Renderer │ │ Scheduler │ │
│ │ (响应式) │ │ (渲染器) │ │ (调度器) │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ DOM / Platform │
└─────────────────────────────────────────────────────────────┘
二、编译时 vs 运行时
2.1 编译时(Compile Time)
// 模板
<template>
<div>{{ msg }}</div>
</template>
// 编译后的 render 函数
function render(_ctx) {
return _createElementVNode("div", null, _toDisplayString(_ctx.msg))
}
2.2 运行时(Runtime)
// 运行时执行 render 函数
const vnode = render(ctx)
// patch 到 DOM
patch(null, vnode, container)
三、响应式系统
3.1 核心 API
// reactive - 对象响应式
const state = reactive({ count: 0 })
// ref - 基本类型响应式
const count = ref(0)
// computed - 计算属性
const double = computed(() => count.value * 2)
// effect - 副作用
effect(() => {
console.log(count.value)
})
3.2 依赖收集与触发
┌─────────────────────────────────────────────────────────────┐
│ 响应式流程 │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────┐ get ┌─────────┐ │
│ │ Proxy │ ─────────► │ track │ ──► 收集当前 effect │
│ └─────────┘ └─────────┘ │
│ │ │
│ │ set │
│ ▼ │
│ ┌─────────┐ ┌─────────┐ │
│ │ trigger │ ─────────► │ effects │ ──► 执行所有 effect │
│ └─────────┘ └─────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
3.3 Dep 与 Effect
// Dep - 依赖容器
class Dep {
subs: Set<Subscriber> // 订阅者集合
track() {
if (activeSub) {
this.subs.add(activeSub)
}
}
trigger() {
this.subs.forEach(sub => sub.notify())
}
}
// ReactiveEffect - 副作用
class ReactiveEffect {
deps: Link[] // 依赖链表
run() {
activeSub = this
return this.fn()
}
notify() {
this.scheduler ? this.scheduler() : this.run()
}
}
四、虚拟 DOM
4.1 VNode 结构
interface VNode {
type: string | Component // 节点类型
props: object | null // 属性
children: VNode[] | string // 子节点
el: Element | null // 真实 DOM
key: string | number // diff key
shapeFlag: number // 节点类型标记
patchFlag: number // 优化标记
}
4.2 ShapeFlags
enum ShapeFlags {
ELEMENT = 1, // 普通元素
FUNCTIONAL_COMPONENT = 1 << 1, // 函数组件
STATEFUL_COMPONENT = 1 << 2, // 有状态组件
TEXT_CHILDREN = 1 << 3, // 文本子节点
ARRAY_CHILDREN = 1 << 4, // 数组子节点
SLOTS_CHILDREN = 1 << 5, // 插槽子节点
TELEPORT = 1 << 6, // Teleport
SUSPENSE = 1 << 7, // Suspense
COMPONENT = STATEFUL_COMPONENT | FUNCTIONAL_COMPONENT
}
五、渲染器
5.1 patch 函数
const patch = (n1, n2, container) => {
if (n1 === n2) return
// 类型不同,卸载旧节点
if (n1 && !isSameVNodeType(n1, n2)) {
unmount(n1)
n1 = null
}
const { type, shapeFlag } = n2
switch (type) {
case Text:
processText(n1, n2, container)
break
case Fragment:
processFragment(n1, n2, container)
break
default:
if (shapeFlag & ShapeFlags.ELEMENT) {
processElement(n1, n2, container)
} else if (shapeFlag & ShapeFlags.COMPONENT) {
processComponent(n1, n2, container)
}
}
}
5.2 组件挂载
const mountComponent = (vnode, container) => {
// 1. 创建组件实例
const instance = createComponentInstance(vnode)
// 2. 设置组件(执行 setup)
setupComponent(instance)
// 3. 设置渲染副作用
setupRenderEffect(instance, vnode, container)
}
const setupRenderEffect = (instance, vnode, container) => {
const effect = new ReactiveEffect(() => {
if (!instance.isMounted) {
// 首次挂载
const subTree = instance.render()
patch(null, subTree, container)
instance.subTree = subTree
instance.isMounted = true
} else {
// 更新
const nextTree = instance.render()
patch(instance.subTree, nextTree, container)
instance.subTree = nextTree
}
})
effect.run()
}
六、调度器
6.1 任务队列
const queue: SchedulerJob[] = []
let isFlushing = false
function queueJob(job) {
if (!queue.includes(job)) {
queue.push(job)
queueFlush()
}
}
function queueFlush() {
if (!isFlushing) {
isFlushing = true
Promise.resolve().then(flushJobs)
}
}
function flushJobs() {
queue.sort((a, b) => getId(a) - getId(b))
for (const job of queue) {
job()
}
queue.length = 0
isFlushing = false
}
6.2 nextTick
const resolvedPromise = Promise.resolve()
function nextTick(fn?) {
return fn
? resolvedPromise.then(fn)
: resolvedPromise
}
七、组件系统
7.1 组件实例
interface ComponentInternalInstance {
uid: number // 唯一 ID
type: Component // 组件定义
parent: ComponentInternalInstance | null
// 状态
data: object // data()
props: object // props
setupState: object // setup() 返回值
ctx: object // 渲染上下文
// 渲染
render: Function // render 函数
subTree: VNode // 渲染的 VNode 树
effect: ReactiveEffect // 渲染副作用
// 生命周期
isMounted: boolean
isUnmounted: boolean
// 生命周期钩子
bc: Function[] | null // beforeCreate
c: Function[] | null // created
bm: Function[] | null // beforeMount
m: Function[] | null // mounted
bu: Function[] | null // beforeUpdate
u: Function[] | null // updated
bum: Function[] | null // beforeUnmount
um: Function[] | null // unmounted
}
7.2 setup 执行
function setupComponent(instance) {
const { props, children } = instance.vnode
// 初始化 props
initProps(instance, props)
// 初始化 slots
initSlots(instance, children)
// 执行 setup
const { setup } = instance.type
if (setup) {
const setupResult = setup(instance.props, {
attrs: instance.attrs,
slots: instance.slots,
emit: instance.emit,
expose: instance.expose
})
handleSetupResult(instance, setupResult)
}
}
八、编译优化
8.1 PatchFlags
enum PatchFlags {
TEXT = 1, // 动态文本
CLASS = 1 << 1, // 动态 class
STYLE = 1 << 2, // 动态 style
PROPS = 1 << 3, // 动态 props
FULL_PROPS = 1 << 4, // 有动态 key
NEED_HYDRATION = 1 << 5,
STABLE_FRAGMENT = 1 << 6,
KEYED_FRAGMENT = 1 << 7,
UNKEYED_FRAGMENT = 1 << 8,
NEED_PATCH = 1 << 9,
DYNAMIC_SLOTS = 1 << 10,
HOISTED = -1, // 静态提升
BAIL = -2 // 退出优化
}
8.2 Block Tree
// 编译优化:只追踪动态节点
const _hoisted_1 = createVNode("div", null, "static")
function render() {
return (openBlock(), createBlock("div", null, [
_hoisted_1, // 静态提升
createVNode("span", null, ctx.msg, PatchFlags.TEXT) // 动态节点
]))
}
九、完整渲染流程
┌─────────────────────────────────────────────────────────────┐
│ Vue 渲染流程 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 1. createApp(App).mount('#app') │
│ │ │
│ ▼ │
│ 2. 创建 VNode │
│ │ │
│ ▼ │
│ 3. render(vnode, container) │
│ │ │
│ ▼ │
│ 4. patch(null, vnode, container) │
│ │ │
│ ▼ │
│ 5. processComponent → mountComponent │
│ │ │
│ ├── createComponentInstance │
│ ├── setupComponent (执行 setup) │
│ └── setupRenderEffect │
│ │ │
│ ▼ │
│ 6. ReactiveEffect.run() │
│ │ │
│ ▼ │
│ 7. instance.render() → subTree VNode │
│ │ │
│ ▼ │
│ 8. patch(null, subTree, container) │
│ │ │
│ ▼ │
│ 9. 递归处理子节点 → 挂载到 DOM │
│ │
└─────────────────────────────────────────────────────────────┘
十、小结
Vue3 架构的核心:
- 响应式系统:基于 Proxy,依赖收集 + 触发更新
- 虚拟 DOM:VNode 描述 UI,patch 算法高效更新
- 编译优化:PatchFlags、Block Tree、静态提升
- 调度器:批量更新,nextTick 微任务队列
- 组件系统:setup + Composition API
📦 源码地址:github.com/vuejs/core
下一篇:响应式系统详解
如果觉得有帮助,欢迎点赞收藏 👍