Monorepo(单一代码库)是现代前端工程化的重要趋势,以下是完整的架构思路和最佳实践方案。
一、Monorepo 核心架构设计
1. 目录结构设计
your-project/
├── packages/ # 所有子包
│ ├── core/ # 核心包
│ │ ├── src/
│ │ ├── package.json
│ │ └── vite.config.ts
│ ├── ui/ # UI组件库
│ │ ├── src/
│ │ ├── package.json
│ │ └── vite.config.ts
│ ├── utils/ # 工具函数
│ │ ├── src/
│ │ └── package.json
│ ├── app1/ # 应用1
│ │ ├── src/
│ │ ├── package.json
│ │ └── vite.config.ts
│ └── app2/ # 应用2
│ ├── src/
│ ├── package.json
│ └── vite.config.ts
├── configs/ # 共享配置
│ ├── eslint/
│ ├── prettier/
│ ├── typescript/
│ └── vite/
├── scripts/ # 构建脚本
│ ├── build.js
│ ├── deploy.js
│ └── release.js
├── docs/ # 文档
├── tests/ # 测试
│ ├── unit/
│ └── e2e/
├── package.json
├── pnpm-workspace.yaml # 工作空间配置
├── turbo.json # Turborepo配置
├── tsconfig.base.json # TypeScript基础配置
└── .npmrc # npm配置
二、技术选型与工具链
1. 包管理工具
pnpm + workspace(推荐)
# pnpm-workspace.yaml
packages:
- 'packages/*'
- 'configs/*'
- 'apps/*'
- 'docs'
// package.json
{
"name": "monorepo-project",
"private": true,
"scripts": {
"dev": "turbo run dev",
"build": "turbo run build",
"test": "turbo run test",
"lint": "turbo run lint",
"changeset": "changeset",
"version": "changeset version",
"release": "turbo run build && changeset publish"
},
"devDependencies": {
"@changesets/cli": "^2.26.0",
"turbo": "^1.10.0",
"typescript": "^5.0.0"
},
"packageManager": "pnpm@8.0.0",
"engines": {
"node": ">=16",
"pnpm": ">=8"
}
}
2. 构建编排工具
Turborepo 配置
// turbo.json
{
"$schema": "https://turbo.build/schema.json",
"globalDependencies": ["**/.env.*local"],
"pipeline": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**", ".next/**", "!.next/cache/**"],
"env": ["NODE_ENV"]
},
"test": {
"dependsOn": ["build"],
"inputs": ["src/**/*.tsx", "src/**/*.ts", "test/**/*.ts"],
"outputs": []
},
"lint": {
"outputs": []
},
"dev": {
"cache": false,
"persistent": true
},
"typecheck": {
"dependsOn": ["^typecheck"],
"outputs": []
}
}
}
3. TypeScript 配置
// tsconfig.base.json
{
"compilerOptions": {
"target": "ES2020",
"module": "ESNext",
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"jsx": "react-jsx",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"moduleResolution": "node",
"allowUnreachableCode": false,
"allowUnusedLabels": false,
"noFallthroughCasesInSwitch": true,
"noImplicitReturns": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"declaration": true,
"declarationMap": true,
"sourceMap": true,
"composite": true,
"incremental": true,
"paths": {
"@core/*": ["./packages/core/src/*"],
"@ui/*": ["./packages/ui/src/*"],
"@utils/*": ["./packages/utils/src/*"]
}
},
"exclude": ["node_modules", "dist"]
}
三、核心包开发规范
1. 共享配置管理
ESLint 共享配置
// configs/eslint/index.js
module.exports = {
env: {
browser: true,
es2021: true,
node: true
},
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:react/recommended',
'prettier'
],
parser: '@typescript-eslint/parser',
parserOptions: {
ecmaFeatures: {
jsx: true
},
ecmaVersion: 12,
sourceType: 'module'
},
plugins: ['@typescript-eslint', 'react', 'react-hooks'],
rules: {
'react/react-in-jsx-scope': 'off',
'react-hooks/rules-of-hooks': 'error',
'react-hooks/exhaustive-deps': 'warn'
},
settings: {
react: {
version: 'detect'
}
}
}
2. 工具包开发示例
// packages/utils/src/request.ts
import axios, { AxiosInstance, AxiosRequestConfig } from 'axios';
export class HttpClient {
private instance: AxiosInstance;
constructor(config?: AxiosRequestConfig) {
this.instance = axios.create({
timeout: 10000,
...config
});
this.setupInterceptors();
}
private setupInterceptors() {
this.instance.interceptors.request.use(
(config) => {
// 添加token等
return config;
},
(error) => Promise.reject(error)
);
}
async get<T>(url: string, params?: any): Promise<T> {
const response = await this.instance.get<T>(url, { params });
return response.data;
}
async post<T>(url: string, data?: any): Promise<T> {
const response = await this.instance.post<T>(url, data);
return response.data;
}
}
export default new HttpClient();
3. UI组件库封装
// packages/ui/src/Button/index.tsx
import React from 'react';
import classNames from 'classnames';
import './style.css';
export interface ButtonProps {
type?: 'primary' | 'default' | 'danger';
size?: 'large' | 'middle' | 'small';
disabled?: boolean;
onClick?: React.MouseEventHandler<HTMLButtonElement>;
children?: React.ReactNode;
}
export const Button: React.FC<ButtonProps> = ({
type = 'default',
size = 'middle',
disabled = false,
onClick,
children
}) => {
const classes = classNames(
'btn',
`btn-${type}`,
`btn-${size}`,
{
'btn-disabled': disabled
}
);
return (
<button
className={classes}
disabled={disabled}
onClick={onClick}
>
{children}
</button>
);
};
Button.displayName = 'Button';
四、应用层开发规范
1. 应用入口配置
// packages/app1/vite.config.ts
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import path from 'path';
export default defineConfig({
plugins: [react()],
resolve: {
alias: {
'@core': path.resolve(__dirname, '../core/src'),
'@ui': path.resolve(__dirname, '../ui/src'),
'@utils': path.resolve(__dirname, '../utils/src')
}
},
server: {
port: 3001,
proxy: {
'/api': {
target: 'http://localhost:8080',
changeOrigin: true
}
}
},
build: {
sourcemap: true,
rollupOptions: {
output: {
manualChunks: {
vendor: ['react', 'react-dom'],
ui: ['@ui']
}
}
}
}
});
2. 环境变量管理
# .env.development
VITE_API_BASE_URL=http://localhost:8080/api
VITE_APP_TITLE=App1 Development
# .env.production
VITE_API_BASE_URL=https://api.example.com
VITE_APP_TITLE=App1 Production
五、版本管理与发布
1. Changesets 配置
// .changeset/config.json
{
"$schema": "https://unpkg.com/@changesets/config@2.3.0/schema.json",
"changelog": "@changesets/cli/changelog",
"commit": false,
"fixed": [
["@project/core", "@project/ui"]
],
"linked": [],
"access": "public",
"baseBranch": "main",
"updateInternalDependencies": "patch",
"ignore": ["@project/app1", "@project/app2"],
"___experimentalUnsafeOptions_WILL_CHANGE_IN_PATCH": {
"onlyUpdatePeerDependentsWhenOutOfRange": true
}
}
2. 发布脚本
// scripts/release.js
const { execSync } = require('child_process');
const fs = require('fs');
const path = require('path');
const version = process.argv[2];
if (!version) {
console.error('请指定版本号');
process.exit(1);
}
try {
// 更新版本号
execSync(`pnpm changeset version ${version}`, { stdio: 'inherit' });
// 构建所有包
execSync('pnpm run build', { stdio: 'inherit' });
// 运行测试
execSync('pnpm run test', { stdio: 'inherit' });
// 发布到npm
execSync('pnpm changeset publish', { stdio: 'inherit' });
// 提交版本更新
execSync('git add .', { stdio: 'inherit' });
execSync(`git commit -m "chore: release v${version}"`, { stdio: 'inherit' });
execSync(`git tag v${version}`, { stdio: 'inherit' });
execSync('git push && git push --tags', { stdio: 'inherit' });
console.log(`✅ 发布成功 v${version}`);
} catch (error) {
console.error('发布失败:', error);
process.exit(1);
}
六、CI/CD 配置
GitHub Actions 示例
# .github/workflows/ci.yml
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: pnpm/action-setup@v2
with:
version: 8
- uses: actions/setup-node@v3
with:
node-version: 18
cache: 'pnpm'
- name: Install dependencies
run: pnpm install
- name: Lint
run: pnpm run lint
- name: Type check
run: pnpm run typecheck
- name: Test
run: pnpm run test
- name: Build
run: pnpm run build
- name: Upload build artifacts
uses: actions/upload-artifact@v3
with:
name: dist
path: |
packages/*/dist
apps/*/dist
deploy:
needs: validate
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Download build artifacts
uses: actions/download-artifact@v3
with:
name: dist
- name: Deploy to server
uses: easingthemes/ssh-deploy@main
with:
SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }}
ARGS: "-rlgoDzvc -i --delete"
SOURCE: "dist/"
REMOTE_HOST: ${{ secrets.REMOTE_HOST }}
REMOTE_USER: ${{ secrets.REMOTE_USER }}
TARGET: ${{ secrets.REMOTE_TARGET }}
七、最佳实践总结
1. 架构原则
- 关注点分离:核心包、UI库、工具函数、业务应用清晰分层
- 依赖明确:使用
pnpm管理内部依赖,避免循环依赖 - 构建优化:利用 Turborepo 缓存,加速构建
- 类型共享:TypeScript 配置统一,类型定义共享
2. 开发规范
- 版本管理:使用 Changesets 统一管理版本发布
- 代码质量:共享 ESLint、Prettier 配置
- 测试策略:单元测试 + E2E 测试结合
- 文档建设:每个包必须有 README 和使用文档
3. 性能优化
- 按需加载:UI库支持按需引入
- 构建缓存:合理配置 Turborepo 缓存策略
- 依赖分析:定期检查和优化依赖关系
- 分包策略:合理的代码分割和公共依赖提取
4. 团队协作
- 代码审查:严格的 PR 审查流程
- 提交规范:使用 Commitlint 规范提交信息
- 变更记录:自动生成 CHANGELOG
- 知识共享:建立技术文档和决策记录
5. 常见问题处理
- 循环依赖:使用
pnpm ls --depth=10检查依赖关系 - 构建速度:合理配置 Turborepo 缓存和并行构建
- 版本冲突:统一核心依赖版本,使用 resolutions 或 overrides
- 类型错误:确保所有包使用相同的 TypeScript 版本
通过以上架构设计和最佳实践,可以构建一个可维护、可扩展、高效的 Monorepo 项目,适合大型团队协作和复杂业务场景。