使用 pnpm workspace 搭建 Vue MVP 的 monorepo 结构

2025-09-14 13:17:28

这一篇先把项目结构搭起来。

如果只是写一个响应式 demo,一个 index.ts 就够了。但这个系列后面还会继续实现运行时和编译时,如果所有代码都堆在一起,很快就会变得不好维护。

所以第一步先使用 monorepo 拆分模块,让每个包只负责一件事。

2025 年了,我不允许你还不会 monorepo

为什么使用 Monorepo

monorepo 指的是在一个 Git 仓库里管理多个相互关联的包。

可以把它理解成一个工厂:里面有多个独立的流水线(子项目),但每一个流水线都有共用的一套标准化工具和基础设施

Monorepo 和 Multi-repo

维度MonorepoMulti-repo
仓库数量一个仓库管理多个包多个仓库分别管理
本地调试可以直接引用 workspace 包通常需要发布、link 或额外配置
版本对齐更容易统一修改和提交跨仓库同步成本更高
工具配置可以共享构建、测试、格式化配置每个仓库都需要维护一份

目录设计

先规划一个可用目录:

packages/
  reactivity/
  runtime-core/
  runtime-dom/
  shared/
  • reactivity:实现 refreactiveeffectcomputed
  • runtime-core:实现 vnode、组件实例、调度器和通用渲染流程
  • runtime-dom:实现 DOM 平台的 rendercreateElement
  • shared:实现 isObjectisFunctionhasChanged 这类通用工具

配置 pnpm workspace

在根目录创建 pnpm-workspace.yaml

packages:
  - packages/*

这段配置的含义是:packages 目录下的每一个子目录都可以作为一个独立 workspace 包。

先创建这两个包:

packages/reactivity
packages/shared

安装公共依赖

typescript 属于整个项目都会用到的开发依赖,所以安装到 workspace 根目录:

pnpm add typescript -D -w
pnpm add @types/node -D -w

配置 tsconfig

{
  "compilerOptions": {
    "target": "ESNext",
    // 指定 ECMAScript 目标版本
    "module": "ESNext",
    // 指定模块代码生成规范
    "moduleResolution": "node",
    // 指定模块解析策略
    "outDir": "dist",
    // 指定编译输出的目录
    "resolveJsonModule": true,
    // 允许导入 JSON 文件
    "strict": false,
    // 关闭严格模式
    "lib": [
      "ESNext",
      "DOM"
    ],
    // 指定要使用的库文件
    "paths": {
      "@vue/*": [
        "packages/*/src"
      ]
    },
    "baseUrl": "./"
  }
}

这里最重要的是 baseUrlpaths

{
  "baseUrl": "./",
  "paths": {
    "@vue/*": [
      "packages/*/src"
    ]
  }
}

这段配置让 TypeScript 能识别 @vue/shared@vue/reactivity 这种包名引用,并把它们映射到本地的 packages/*/src 目录。