Skip to content

1.1 Monorepo架构:Vue 3 的工程化基石

引言

Vue 3 采用了 Monorepo(单一仓库)架构,这是一个重要的工程化决策。相比 Vue 2 的单体架构,Vue 3 将整个框架拆分为多个独立的包,每个包都有明确的职责边界和依赖关系。这种架构不仅提升了代码的可维护性,也为框架的模块化使用和 Tree-shaking 优化奠定了基础。

Monorepo 架构概览

工作区配置

Vue 3 使用 pnpm 作为包管理器,通过 pnpm-workspace.yaml 文件定义工作区结构:

yaml
packages:
  - 'packages/*'
  - 'packages-private/*'

catalog:
  '@babel/parser': ^7.28.3
  '@babel/types': ^7.28.2
  'estree-walker': ^2.0.2
  'magic-string': ^0.30.18
  'source-map-js': ^1.2.1
  'vite': ^5.4.15
  '@vitejs/plugin-vue': ^6.0.1

这个配置文件定义了两个主要的包目录:

  • packages/*:公开发布的核心包
  • packages-private/*:内部工具和测试包

catalog 字段统一管理共享依赖的版本,确保整个 Monorepo 中依赖版本的一致性。

核心包结构解析

Vue 3 的 packages 目录包含以下核心包:

1. @vue/shared - 共享工具包

json
{
  "name": "@vue/shared",
  "description": "internal utils shared across @vue packages",
  "buildOptions": {
    "formats": ["esm-bundler", "cjs"]
  },
  "sideEffects": false
}

作为基础依赖,shared 包提供了框架内部使用的工具函数,如类型判断、对象操作等。它没有任何外部依赖,是整个依赖图的根节点。

2. @vue/reactivity - 响应式系统

json
{
  "name": "@vue/reactivity",
  "buildOptions": {
    "name": "VueReactivity",
    "formats": ["esm-bundler", "esm-browser", "cjs", "global"]
  }
}

响应式系统是 Vue 3 的核心创新,可以独立使用。支持多种构建格式,包括浏览器直接使用的 global 格式。

3. @vue/runtime-core - 平台无关运行时

json
{
  "name": "@vue/runtime-core",
  "dependencies": {
    "@vue/shared": "workspace:*",
    "@vue/reactivity": "workspace:*"
  },
  "buildOptions": {
    "formats": ["esm-bundler", "cjs"]
  }
}

运行时核心包含组件系统、虚拟 DOM、调度器等核心逻辑,依赖于 sharedreactivity 包。

4. @vue/runtime-dom - DOM 平台运行时

扩展 runtime-core,提供 DOM 特定的实现,如事件处理、属性操作等。

5. @vue/compiler-core - 编译器核心

包含模板解析、AST 转换、代码生成等编译器核心逻辑。

6. @vue/compiler-dom - DOM 编译器

扩展编译器核心,提供 DOM 特定的编译逻辑。

7. vue - 完整构建

json
{
  "name": "vue",
  "main": "index.js",
  "module": "dist/vue.runtime.esm-bundler.js",
  "exports": {
    ".": {
      "import": {
        "types": "./dist/vue.d.mts",
        "node": "./index.mjs",
        "default": "./dist/vue.runtime.esm-bundler.js"
      },
      "require": {
        "types": "./dist/vue.d.ts",
        "default": "./index.js"
      }
    }
  }
}

主包整合所有子包,提供完整的 Vue 功能。

依赖关系分析

依赖图谱

Vue 3 的包依赖关系呈现清晰的层次结构:

vue (完整构建)
├── @vue/runtime-dom
│   └── @vue/runtime-core
│       ├── @vue/reactivity
│       │   └── @vue/shared
│       └── @vue/shared
├── @vue/compiler-dom
│   └── @vue/compiler-core
│       └── @vue/shared
└── @vue/server-renderer

循环依赖避免策略

  1. 单向依赖原则:严格遵循从上层到下层的单向依赖
  2. 共享基础包:通过 @vue/shared 提供公共工具,避免包间直接依赖
  3. 接口抽象:使用 TypeScript 接口定义包间契约

构建顺序优化

构建脚本 build.js 通过依赖分析确定构建顺序:

javascript
// 构建目标解析
const resolvedTargets = targets.length
  ? fuzzyMatchTarget(targets, buildAllMatching)
  : allTargets

// 并行构建优化
async function runParallel(maxConcurrency, source, iteratorFn) {
  const ret = []
  const executing = []
  for (const item of source) {
    const p = Promise.resolve().then(() => iteratorFn(item, source))
    ret.push(p)
    if (source.length >= maxConcurrency) {
      executing.push(p.then(() => executing.splice(executing.indexOf(p), 1)))
    }
    if (executing.length >= maxConcurrency) {
      await Promise.race(executing)
    }
  }
  await Promise.all(ret)
  return ret
}

构建系统深度解析

Rollup 配置策略

Vue 3 使用 Rollup 作为构建工具,通过 rollup.config.js 定义构建配置:

javascript
/** @type {Record<PackageFormat, OutputOptions>} */
const outputConfigs = {
  'esm-bundler': {
    file: resolve(`dist/${name}.esm-bundler.js`),
    format: 'es',
  },
  'esm-browser': {
    file: resolve(`dist/${name}.esm-browser.js`),
    format: 'es',
  },
  cjs: {
    file: resolve(`dist/${name}.cjs.js`),
    format: 'cjs',
  },
  global: {
    file: resolve(`dist/${name}.global.js`),
    format: 'iife',
  },
}

多格式输出策略

  1. ESM Bundler:供打包工具使用,保留导入/导出语句
  2. ESM Browser:供现代浏览器直接使用,内联依赖
  3. CommonJS:供 Node.js 环境使用
  4. Global/IIFE:供传统浏览器通过 script 标签使用

Tree-shaking 优化

javascript
// package.json 中的 sideEffects 声明
{
  "sideEffects": false
}

// 构建配置中的优化
function createConfig(format, output, plugins = []) {
  const isProductionBuild = process.env.__DEV__ === 'false'
  const isGlobalBuild = /global/.test(format)
  const isRawESMBuild = format === 'esm-bundler'
  
  if (isGlobalBuild) {
    output.name = packageOptions.name
  }
  
  const shouldEmitDeclarations = process.env.TYPES != null && !hasTSChecked
  
  const tsPlugin = esbuild({
    tsconfig: path.resolve(__dirname, 'tsconfig.json'),
    sourceMap: output.sourcemap,
    minify: false,
    target: isServerRenderer || isNodeBuild ? 'node14' : 'es2015',
    define: resolveDefine(),
  })
}

架构优势对比分析

Vue 2 vs Vue 3 架构对比

方面Vue 2Vue 3
架构模式单体架构Monorepo
包管理单一 npm 包多个独立包
模块化运行时拆分编译时 + 运行时拆分
Tree-shaking有限支持完全支持
独立使用不支持支持(如 @vue/reactivity)
类型支持外部 @types内置 TypeScript

与其他框架对比

React

  • 采用 Monorepo,但包粒度更粗
  • 主要分为 react 和 react-dom
  • 调度器等核心逻辑内置

Angular

  • 完全的 Monorepo 架构
  • 包粒度极细,功能高度模块化
  • 依赖注入系统复杂

Vue 3 的平衡

  • 适中的包粒度,既保证模块化又避免过度复杂
  • 清晰的依赖层次,易于理解和维护
  • 渐进式使用,可按需引入

开发体验和维护性提升

开发体验优化

  1. 独立开发:每个包可以独立开发和测试
  2. 增量构建:只构建变更的包,提升构建效率
  3. 类型安全:完整的 TypeScript 支持
  4. 调试友好:清晰的包边界便于问题定位

维护性提升

  1. 职责清晰:每个包有明确的功能边界
  2. 版本管理:统一的版本发布策略
  3. 测试隔离:包级别的单元测试
  4. 文档完善:每个包都有独立的文档

实践指导

本地开发环境搭建

bash
# 1. 克隆仓库
git clone https://github.com/vuejs/core.git
cd core

# 2. 安装依赖
pnpm install

# 3. 构建所有包
pnpm build

# 4. 开发模式(监听文件变化)
pnpm dev

# 5. 运行测试
pnpm test

包间调试技巧

bash
# 构建特定包
pnpm build reactivity

# 构建多个包
pnpm build reactivity runtime-core

# 指定构建格式
pnpm build reactivity --formats esm-bundler

# 开发模式构建
pnpm build reactivity -d

# 生产模式构建
pnpm build reactivity -p

贡献代码流程

  1. Fork 仓库:在 GitHub 上 fork Vue 仓库
  2. 创建分支:基于 main 分支创建功能分支
  3. 本地开发:使用 pnpm dev 进行开发
  4. 运行测试:确保所有测试通过
  5. 提交代码:遵循 conventional commits 规范
  6. 创建 PR:提交 Pull Request

自定义构建配置

javascript
// 在包的 package.json 中配置构建选项
{
  "buildOptions": {
    "name": "MyVuePackage",
    "formats": ["esm-bundler", "cjs"],
    "filename": "my-package"
  }
}

总结

Vue 3 的 Monorepo 架构是一个精心设计的工程化方案,它在模块化、可维护性和开发体验之间找到了最佳平衡点。通过清晰的包结构、合理的依赖关系和强大的构建系统,Vue 3 不仅提升了框架本身的质量,也为开发者提供了更好的使用体验。

这种架构设计的核心思想是关注点分离渐进式增强,每个包都有明确的职责边界,开发者可以根据需要选择合适的包进行使用。同时,统一的构建系统和版本管理策略确保了整个生态系统的一致性和稳定性。

在后续章节中,我们将深入探讨各个核心包的实现细节,了解 Vue 3 如何通过这种架构实现高性能、高可维护性的现代前端框架。


微信公众号二维码