Vue.js 源码揭秘(一):Vue3 架构总览

Vue.js 源码揭秘(一):Vue3 架构总览

本文从全局视角解析 Vue3 的核心架构,建立源码阅读的整体认知。

一、整体架构

scss 复制代码
┌─────────────────────────────────────────────────────────────┐
│                      Vue Application                        │
└─────────────────────────────────────────────────────────────┘
                           │
                           ▼
┌─────────────────────────────────────────────────────────────┐
│                    Compiler (编译时)                         │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐         │
│  │   Parse     │─►│  Transform  │─►│   Codegen   │         │
│  │  (解析)     │  │   (转换)    │  │  (代码生成)  │         │
│  └─────────────┘  └─────────────┘  └─────────────┘         │
└─────────────────────────────────────────────────────────────┘
                           │
                           ▼ render function
┌─────────────────────────────────────────────────────────────┐
│                    Runtime (运行时)                          │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐         │
│  │ Reactivity  │  │   Renderer  │  │  Scheduler  │         │
│  │  (响应式)   │  │   (渲染器)   │  │   (调度器)   │         │
│  └─────────────┘  └─────────────┘  └─────────────┘         │
└─────────────────────────────────────────────────────────────┘
                           │
                           ▼
┌─────────────────────────────────────────────────────────────┐
│                      DOM / Platform                         │
└─────────────────────────────────────────────────────────────┘

二、编译时 vs 运行时

2.1 编译时(Compile Time)

typescript 复制代码
// 模板
<template>
  <div>{{ msg }}</div>
</template>

// 编译后的 render 函数
function render(_ctx) {
  return _createElementVNode("div", null, _toDisplayString(_ctx.msg))
}

2.2 运行时(Runtime)

typescript 复制代码
// 运行时执行 render 函数
const vnode = render(ctx)

// patch 到 DOM
patch(null, vnode, container)

三、响应式系统

3.1 核心 API

typescript 复制代码
// 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 依赖收集与触发

sql 复制代码
┌─────────────────────────────────────────────────────────────┐
│                    响应式流程                                │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│   ┌─────────┐    get     ┌─────────┐                       │
│   │  Proxy  │ ─────────► │  track  │ ──► 收集当前 effect    │
│   └─────────┘            └─────────┘                       │
│        │                                                    │
│        │ set                                                │
│        ▼                                                    │
│   ┌─────────┐            ┌─────────┐                       │
│   │ trigger │ ─────────► │ effects │ ──► 执行所有 effect    │
│   └─────────┘            └─────────┘                       │
│                                                             │
└─────────────────────────────────────────────────────────────┘

3.3 Dep 与 Effect

typescript 复制代码
// 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 结构

typescript 复制代码
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

typescript 复制代码
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 函数

typescript 复制代码
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 组件挂载

typescript 复制代码
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 任务队列

typescript 复制代码
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

typescript 复制代码
const resolvedPromise = Promise.resolve()

function nextTick(fn?) {
  return fn 
    ? resolvedPromise.then(fn) 
    : resolvedPromise
}

七、组件系统

7.1 组件实例

typescript 复制代码
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 执行

typescript 复制代码
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

typescript 复制代码
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

typescript 复制代码
// 编译优化:只追踪动态节点
const _hoisted_1 = createVNode("div", null, "static")

function render() {
  return (openBlock(), createBlock("div", null, [
    _hoisted_1,  // 静态提升
    createVNode("span", null, ctx.msg, PatchFlags.TEXT)  // 动态节点
  ]))
}

九、完整渲染流程

scss 复制代码
┌─────────────────────────────────────────────────────────────┐
│                    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 架构的核心:

  1. 响应式系统:基于 Proxy,依赖收集 + 触发更新
  2. 虚拟 DOM:VNode 描述 UI,patch 算法高效更新
  3. 编译优化:PatchFlags、Block Tree、静态提升
  4. 调度器:批量更新,nextTick 微任务队列
  5. 组件系统:setup + Composition API

📦 源码地址:github.com/vuejs/core

下一篇:响应式系统详解

如果觉得有帮助,欢迎点赞收藏 👍

相关推荐
千寻girling2 小时前
面试官: “ 请你说一下什么是 ajax ? ”
前端·javascript
MQliferecord2 小时前
webpack的生命周期与Loader/Plugin
前端
@大迁世界2 小时前
JavaScript 框架的终结
开发语言·前端·javascript·ecmascript
PPPPickup2 小时前
easychat项目复盘---管理端系统设置
java·开发语言·前端
梦6502 小时前
Vue 实现动态路由
前端·javascript·vue.js
姜糖编程日记2 小时前
C++——初识(2)
开发语言·前端·c++
丶乘风破浪丶2 小时前
Vue项目中判断相同请求的实现方案:从原理到实战
前端·javascript·vue.js
why技术2 小时前
如果让我站在科技从业者的角度去回看 2025 年,让我选一个词出来形容它,我会选择“vibe coding”这个词。
前端·后端·程序员
worxfr3 小时前
CSS Flexbox 布局完全指南
前端·css