2026.01.01

运行时模块 -> Vue 核心架构设计与拆分

为什么要拆分模块?

虽然前面的代码已经能够工作了,但与 Vue 源码的目录结构相比还有差距。为了更好地理解 Vue 的架构设计,我们按照源码的方式进行模块拆分。

Vue 的模块依赖关系

在 Vue 3 中,各模块之间的依赖关系如下:

reactivity (响应式系统)
    ↓
runtime-core (核心渲染逻辑)
    ↓
runtime-dom (浏览器 DOM 操作)
    ↓
vue (对外暴露的完整包)

具体来说:

  • runtime-core 依赖 reactivity
  • runtime-dom 依赖 runtime-core
  • vue 包导出 runtime-dom 的所有内容

创建模块结构

packages 目录下创建 runtime-domruntime-core 两个模块:

.
├── package.json
├── packages
│   ├── reactivity              # 响应式模块
│   │   ├── package.json
│   │   └── src
│   │       ├── baseHandlers.ts
│   │       ├── computed.ts
│   │       ├── constant.ts
│   │       ├── dep.ts
│   │       ├── effect.ts
│   │       ├── index.ts
│   │       ├── reactive.ts
│   │       ├── ref.ts
│   │       ├── system.ts
│   │       └── watch.ts
│   ├── runtime-core            # 核心渲染模块 (vnode、diff)
│   │   ├── package.json
│   │   └── src
│   │       └── index.ts
│   ├── runtime-dom             # DOM 相关操作
│   │   ├── package.json
│   │   └── src
│   │       ├── index.ts
│   │       └── nodeOps.ts
│   ├── shared                  # 工具库
│   │   ├── package.json
│   │   └── src
│   │       └── index.ts
│   └── vue                     # 对外暴露的完整包
│       ├── package.json
│       └── src
│           └── index.ts
├── pnpm-lock.yaml
├── pnpm-workspace.yaml
├── scripts
│   └── dev.js
└── tsconfig.json

迁移代码

将之前实现的 insertcreateElement 等 DOM 操作方法迁移到 nodeOps.ts 中。然后根据依赖关系,在各个模块之间建立依赖。

安装依赖

使用 pnpm workspace 安装模块间的依赖:

# runtime-core 依赖 reactivity
pnpm install --workspace --filter @vue/runtime-core @vue/reactivity

# runtime-dom 依赖 runtime-core
pnpm install --workspace --filter @vue/runtime-dom @vue/runtime-core

# vue 依赖 runtime-dom
pnpm install --workspace --filter vue @vue/runtime-dom

更新代码

完成模块拆分后,更新各模块的 index.ts 以及测试代码:

import { nodeOps } from '../dist/vue.esm.js'

const vnode = h('div', null, 'hello world')

// 使用导出的 nodeOps 创建渲染器
const renderer = createRenderer(nodeOps)

// 渲染虚拟节点
renderer.render(vnode, app)

// 2 秒后卸载
setTimeout(() => {
  renderer.render(null, app)
}, 2000)

总结

通过模块拆分,我们实现了:

  1. 清晰的职责划分:每个模块各司其职
  2. 合理的依赖关系:避免循环依赖
  3. 更好的可维护性:代码结构更接近 Vue 源码
  4. 跨平台能力runtime-core 不依赖浏览器环境,可用于 SSR 等场景