前端工程化全景:从代码规范到 CI/CD 流水线

全面梳理企业级前端工程化体系:Monorepo 与 Polyrepo 选型、包管理策略(npm/yarn/pnpm/bun)、代码规范工具链(ESLint/Prettier/Husky/lint-staged)、Git 工作流(Git Flow/GitHub Flow/trunk-based)、标准化 CI/CD 流水线(GitHub Actions/GitLab CI)、Docker 化部署与环境管理。附 Vue/React 项目实战模板。

工程化的终极目标不是「快」,而是「可复制的高质量交付」。 一套成熟的工程化体系能让新成员 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(统一版本)          │   适合:独立团队、独立发布周期
└──────────────────────────────┘
适合:关联紧密、频繁协同的项目群
维度MonorepoPolyrepo
代码复用容易(本地引用)需要 publish 到 registry
跨包重构原子提交,安全多仓库协调,风险高
CI/CD统一但复杂简单但重复
权限隔离需工具辅助Git 原生隔离
构建缓存Turborepo/Nx 共享各自独立
适用场景产品群、组件库、工具链独立业务、外部开源

1.2 主流 Monorepo 工具

工具特点推荐场景
pnpm workspaces轻量、零配置、原生依赖去重小型到中型项目,首选
Turborepo任务调度 + 远程缓存 + 增量构建大型项目,构建性能敏感
Nx全功能(构建、测试、Lint、图分析)企业级,需要强约束
Rush/Bit微软出品/组件驱动开发微软生态/组件平台
BazelGoogle 出品,极致增量超大规模,学习成本高

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 对比

特性npmyarn (v4)pnpmbun
安装速度快(PnP/Plug’n’Play)极快(硬链接)最快(原生二进制)
磁盘占用大(重复依赖)小(全局 Store)
workspace原生支持原生支持原生支持原生支持
lockfilepackage-lock.jsonyarn.lockpnpm-lock.yamlbun.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 Flowfeature → develop → release → master计划发布传统软件、版本化产品
GitHub Flowfeature → main(PR 合并即发布)持续发布SaaS、Web 应用
Trunk-basedfeature → 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 workspacespnpm
依赖版本锁定(lockfile)pnpm-lock.yaml
ESLint + TypeScript 严格模式eslint.config.js
Prettier 格式化prettier.config.js
提交前拦截(husky + lint-staged).husky/
提交信息规范(commitlint)commitlint.config.js
CI 自动 Lint / Test / BuildGitHub Actions
分支保护 + Code ReviewGitHub Settings
多环境配置隔离.env.*
Docker 多阶段构建Dockerfile
构建产物分析rollup-plugin-visualizer
监控与错误上报Sentry / LogRocket

参考与延伸阅读

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「frontend」更多文章

  1. API 设计与 BFF 层:REST、GraphQL、tRPC 选型与前后端协作
  2. WebAssembly 前端工程化实践:编译链、性能对比与混合架构
  3. 现代浏览器 API 与 Web 平台能力地图