2026.01.01
运行时模块 -> Vue 核心架构设计与拆分
为什么要拆分模块?
虽然前面的代码已经能够工作了,但与 Vue 源码的目录结构相比还有差距。为了更好地理解 Vue 的架构设计,我们按照源码的方式进行模块拆分。
Vue 的模块依赖关系
在 Vue 3 中,各模块之间的依赖关系如下:
reactivity (响应式系统)
↓
runtime-core (核心渲染逻辑)
↓
runtime-dom (浏览器 DOM 操作)
↓
vue (对外暴露的完整包)
具体来说:
runtime-core依赖reactivityruntime-dom依赖runtime-corevue包导出runtime-dom的所有内容
创建模块结构
在 packages 目录下创建 runtime-dom 和 runtime-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
迁移代码
将之前实现的 insert、createElement 等 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)
总结
通过模块拆分,我们实现了:
- 清晰的职责划分:每个模块各司其职
- 合理的依赖关系:避免循环依赖
- 更好的可维护性:代码结构更接近 Vue 源码
- 跨平台能力:
runtime-core不依赖浏览器环境,可用于 SSR 等场景