工程化的终极目标不是「快」,而是「可复制的高质量交付」。 一套成熟的工程化体系能让新成员 5 分钟参与开发、让 Bug 在提交前被拦截、让发布变成一键操作。
一、Monorepo 与 Polyrepo 选型
1.1 两种模式对比
Monorepo(单仓库多包) Polyrepo(多仓库独立)
┌──────────────────────────────┐ ┌─────────┐ ┌─────────┐
│ my-org/ │ │ app-a/ │ │ app-b/ │
│ ├── apps/web/ │ ├─────────┤ ├─────────┤
│ ├── apps/mobile/ │ │ .git │ │ .git │
│ ├── packages/ui/ │ │ 独立 CD │ │ 独立 CD │
│ ├── packages/utils/ │ └─────────┘ └─────────┘
│ ├── packages/config/ │
│ └── .git(统一版本) │ 适合:独立团队、独立发布周期
└──────────────────────────────┘
适合:关联紧密、频繁协同的项目群
| 维度 | Monorepo | Polyrepo |
|---|---|---|
| 代码复用 | 容易(本地引用) | 需要 publish 到 registry |
| 跨包重构 | 原子提交,安全 | 多仓库协调,风险高 |
| CI/CD | 统一但复杂 | 简单但重复 |
| 权限隔离 | 需工具辅助 | Git 原生隔离 |
| 构建缓存 | Turborepo/Nx 共享 | 各自独立 |
| 适用场景 | 产品群、组件库、工具链 | 独立业务、外部开源 |
1.2 主流 Monorepo 工具
| 工具 | 特点 | 推荐场景 |
|---|---|---|
| pnpm workspaces | 轻量、零配置、原生依赖去重 | 小型到中型项目,首选 |
| Turborepo | 任务调度 + 远程缓存 + 增量构建 | 大型项目,构建性能敏感 |
| Nx | 全功能(构建、测试、Lint、图分析) | 企业级,需要强约束 |
| Rush/Bit | 微软出品/组件驱动开发 | 微软生态/组件平台 |
| Bazel | Google 出品,极致增量 | 超大规模,学习成本高 |
1.3 pnpm workspaces 配置实战
# pnpm-workspace.yaml
packages:
- 'apps/*'
- 'packages/*'
- '!**/dist/**'
- '!**/node_modules/**'
// package.json(根)
{
"name": "@my-org/root",
"private": true,
"packageManager": "pnpm@10.2.0",
"scripts": {
"dev:web": "pnpm --filter @my-org/web dev",
"dev:admin": "pnpm --filter @my-org/admin dev",
"build": "pnpm -r build",
"test": "pnpm -r test",
"lint": "pnpm -r lint",
"clean": "pnpm -r exec rm -rf node_modules dist .turbo"
},
"devDependencies": {
"turbo": "^2.3.0"
}
}
// turbo.json(Turborepo 配置)
{
"$schema": "https://turbo.build/schema.json",
"globalDependencies": ["**/.env.*local"],
"pipeline": {
"build": {
"dependsOn": ["^build"],
"outputs": [".next/**", "!.next/cache/**", "dist/**"]
},
"lint": {},
"test": {},
"dev": {
"cache": false,
"persistent": true
}
}
}
二、包管理策略
2.1 npm / yarn / pnpm / bun 对比
| 特性 | npm | yarn (v4) | pnpm | bun |
|---|---|---|---|---|
| 安装速度 | 慢 | 快(PnP/Plug’n’Play) | 极快(硬链接) | 最快(原生二进制) |
| 磁盘占用 | 大(重复依赖) | 中 | 小(全局 Store) | 小 |
| workspace | 原生支持 | 原生支持 | 原生支持 | 原生支持 |
| lockfile | package-lock.json | yarn.lock | pnpm-lock.yaml | bun.lockb |
| 幽灵依赖 | 存在 | PnP 消除 | 严格(.pnpm 隔离) | 严格 |
| 兼容性 | 最好 | 好 | 好 | 较新,生态 building |
幽灵依赖:项目中使用了未在
package.json中声明的依赖(因扁平化安装被 hoist 到顶层),导致构建/运行潜在风险。pnpm 通过.pnpm严格隔离彻底消除。
2.2 pnpm 核心命令速查
# 安装全部依赖( monorepo 根)
pnpm install
# 安装单个包到某个 workspace
pnpm --filter @my-org/web add axios
# 安装 devDependency
pnpm add -D typescript --filter @my-org/ui
# 运行 workspace 脚本
pnpm --filter @my-org/web run build
# 批量运行
pnpm -r run lint
# 依赖分析
pnpm why lodash
pnpm list --depth=0
三、代码规范工具链
3.1 ESLint:静态代码分析
# Vue 3 项目初始化
pnpm create vite@latest my-vue-app -- --template vue-ts
cd my-vue-app
pnpm install -D eslint @eslint/js typescript-eslint eslint-plugin-vue
// eslint.config.js (Flat Config — ESLint v9+)
import js from '@eslint/js';
import ts from 'typescript-eslint';
import vue from 'eslint-plugin-vue';
import prettier from 'eslint-config-prettier';
export default [
js.configs.recommended,
...ts.configs.recommended,
...vue.configs['flat/recommended'],
{
files: ['**/*.{ts,tsx,vue}'],
languageOptions: {
parserOptions: { project: './tsconfig.json' }
},
rules: {
'@typescript-eslint/no-explicit-any': 'warn',
'@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }],
'vue/multi-word-component-names': 'off',
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off'
}
},
prettier // 关闭与 Prettier 冲突的规则
];
3.2 Prettier:代码格式化
// prettier.config.js
export default {
semi: true,
singleQuote: true,
tabWidth: 2,
trailingComma: 'es5',
printWidth: 100,
arrowParens: 'avoid',
endOfLine: 'lf',
plugins: ['prettier-plugin-tailwindcss'] // 自动排序 Tailwind classes
};
# 格式化全部
npx prettier --write .
# 检查(CI 中用)
npx prettier --check .
3.3 Husky + lint-staged:提交前拦截
# 安装
pnpm add -D husky lint-staged
# 初始化 husky(v9+)
npx husky init
// package.json
{
"lint-staged": {
"*.{js,ts,vue,jsx,tsx}": ["eslint --fix", "prettier --write"],
"*.{css,scss,json,md}": ["prettier --write"]
}
}
# .husky/pre-commit(Husky v9 自动生成,或手动配置)
echo "npx lint-staged" > .husky/pre-commit
# .husky/commit-msg(提交信息规范检查)
echo 'npx commitlint --edit ${1}' > .husky/commit-msg
3.4 commitlint:提交信息规范
pnpm add -D @commitlint/config-conventional @commitlint/cli
// commitlint.config.js
export default {
extends: ['@commitlint/config-conventional'],
rules: {
'type-enum': [2, 'always', [
'feat', 'fix', 'docs', 'style', 'refactor',
'perf', 'test', 'chore', 'ci', 'build', 'revert'
]],
'subject-case': [0]
}
};
提交格式:
feat: 新增用户登录功能
fix(auth): 修复 Token 过期未自动刷新
refactor(ui): 重构 Button 组件为复合组件模式
docs(readme): 更新部署说明
test(utils): 增加 dateFormat 单元测试
四、Git 工作流
4.1 三种主流模型
| 模型 | 分支策略 | 发布节奏 | 适用 |
|---|---|---|---|
| Git Flow | feature → develop → release → master | 计划发布 | 传统软件、版本化产品 |
| GitHub Flow | feature → main(PR 合并即发布) | 持续发布 | SaaS、Web 应用 |
| Trunk-based | feature → main(短生命周期分支) | 每日多次 | 大型团队、CI/CD 成熟 |
4.2 GitHub Flow 实战
1. 从 main 创建 feature 分支
git checkout -b feat/user-profile-page
2. 开发并提交(遵循 commitlint 规范)
git commit -m "feat: 新增用户资料页面布局"
3. 推送并创建 Pull Request
git push origin feat/user-profile-page
→ GitHub 上点击 "Create Pull Request"
4. CI 自动运行(Lint / Test / Build)
→ 全部通过方可合并
5. Code Review(最小 1 人 Approve)
→ Reviewer 检查逻辑、测试、性能影响
6. Squash & Merge 到 main
→ 保持 main 历史线性清晰
7. 自动触发部署流水线
4.3 分支保护规则(GitHub)
# .github/settings.yml(或通过 UI 配置)
branches:
- name: main
protection:
required_pull_request_reviews:
required_approving_review_count: 1
required_status_checks:
strict: true
contexts:
- lint
- test
- build
enforce_admins: false
required_linear_history: true
五、标准化 CI/CD 流水线
5.1 GitHub Actions 完整流水线
# .github/workflows/ci.yml
name: CI
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
with: { version: '10.2.0' }
- uses: actions/setup-node@v4
with: { node-version: '22', cache: 'pnpm' }
- run: pnpm install --frozen-lockfile
- run: pnpm lint
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
with: { version: '10.2.0' }
- uses: actions/setup-node@v4
with: { node-version: '22', cache: 'pnpm' }
- run: pnpm install --frozen-lockfile
- run: pnpm test:unit --coverage
- uses: codecov/codecov-action@v5
with: { token: ${{ secrets.CODECOV_TOKEN }} }
build:
runs-on: ubuntu-latest
needs: [lint, test]
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
with: { version: '10.2.0' }
- uses: actions/setup-node@v4
with: { node-version: '22', cache: 'pnpm' }
- run: pnpm install --frozen-lockfile
- run: pnpm build
- uses: actions/upload-artifact@v4
with:
name: dist
path: dist/
5.2 多环境 CD(Staging / Production)
# .github/workflows/deploy.yml
name: Deploy
on:
push:
branches: [main]
jobs:
deploy-staging:
runs-on: ubuntu-latest
environment: staging
steps:
- uses: actions/checkout@v4
- name: Deploy to Vercel (Staging)
run: npx vercel --token ${{ secrets.VERCEL_TOKEN }} --yes
deploy-production:
runs-on: ubuntu-latest
needs: deploy-staging
environment: production # 需要人工审批
steps:
- uses: actions/checkout@v4
- name: Deploy to Vercel (Production)
run: npx vercel --prod --token ${{ secrets.VERCEL_TOKEN }} --yes
5.3 Docker 化前端构建
# Dockerfile(多阶段构建)
# ---- 构建阶段 ----
FROM node:22-alpine AS builder
WORKDIR /app
RUN corepack enable && corepack prepare pnpm@10.2.0 --activate
COPY pnpm-lock.yaml package.json pnpm-workspace.yaml ./
COPY packages ./packages
COPY apps/web ./apps/web
RUN pnpm install --frozen-lockfile
RUN pnpm --filter @my-org/web run build
# ---- 运行阶段 ----
FROM nginx:alpine
COPY --from=builder /app/apps/web/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
# nginx.conf(SPA 配置)
server {
listen 80;
root /usr/share/nginx/html;
index index.html;
gzip on;
gzip_types text/plain text/css application/json application/javascript text/xml;
location / {
try_files $uri $uri/ /index.html;
}
# 静态资源强缓存
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
}
六、环境管理
6.1 多环境配置
.env # 默认(不提交 Git)
.env.local # 本地覆盖(.gitignore)
.env.development # dev 服务器
.env.staging # 预发布
.env.production # 生产
# Vite 环境变量(必须以 VITE_ 前缀暴露给客户端)
VITE_API_BASE_URL=https://api.example.com
VITE_APP_NAME=MyApp
VITE_SENTRY_DSN=https://xxx@xxx.ingest.sentry.io/xxx
# 服务端专用(无前缀)
DATABASE_URL=postgresql://...
6.2 feature flags(功能开关)
// config/features.ts
export const features = {
newDashboard: import.meta.env.VITE_FF_NEW_DASHBOARD === 'true',
betaPayment: import.meta.env.VITE_FF_BETA_PAYMENT === 'true',
} as const;
// 使用
import { features } from '@/config/features';
function App() {
return (
<>
{features.newDashboard ? <NewDashboard /> : <OldDashboard />}
</>
);
}
七、Vue / React 项目工程化模板
Vue 3 + Vite + pnpm 项目结构
my-vue-app/
├── .github/workflows/ci.yml
├── .husky/
│ ├── pre-commit
│ └── commit-msg
├── public/
├── src/
│ ├── assets/
│ ├── components/
│ ├── composables/ # Vue 组合式函数
│ ├── views/
│ ├── router/
│ ├── stores/ # Pinia
│ ├── utils/
│ ├── types/
│ ├── App.vue
│ └── main.ts
├── .env*
├── .prettierrc
├── eslint.config.js
├── commitlint.config.js
├── tsconfig.json
├── vite.config.ts
└── package.json
关键配置速查
// vite.config.ts(Vue 项目)
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import { resolve } from 'path';
export default defineConfig({
plugins: [vue()],
resolve: {
alias: { '@': resolve(__dirname, 'src') }
},
build: {
target: 'es2022',
minify: 'terser',
rollupOptions: {
output: {
manualChunks: {
vendor: ['vue', 'vue-router', 'pinia'],
ui: ['element-plus'] // 或 'ant-design-vue' / 'vuetify'
}
}
}
},
server: {
port: 3000,
proxy: {
'/api': {
target: 'http://localhost:8080',
changeOrigin: true
}
}
}
});
八、工程化 Checklist
| 检查项 | 状态 | 工具 |
|---|---|---|
| 包管理使用 pnpm workspaces | ☐ | pnpm |
| 依赖版本锁定(lockfile) | ☐ | pnpm-lock.yaml |
| ESLint + TypeScript 严格模式 | ☐ | eslint.config.js |
| Prettier 格式化 | ☐ | prettier.config.js |
| 提交前拦截(husky + lint-staged) | ☐ | .husky/ |
| 提交信息规范(commitlint) | ☐ | commitlint.config.js |
| CI 自动 Lint / Test / Build | ☐ | GitHub Actions |
| 分支保护 + Code Review | ☐ | GitHub Settings |
| 多环境配置隔离 | ☐ | .env.* |
| Docker 多阶段构建 | ☐ | Dockerfile |
| 构建产物分析 | ☐ | rollup-plugin-visualizer |
| 监控与错误上报 | ☐ | Sentry / LogRocket |
参考与延伸阅读
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。