前端工程化实战:从脚手架到 Monorepo 的完整演进
前端工程化实战:从脚手架到 Monorepo 的完整演进
前端工程化的本质,是把"每个人都能跑起来的项目"变成"每个人都能维护好的项目"——统一工具链、规范代码风格、自动化质量门禁。本文从单项目的工程基线讲起,延伸到 Monorepo 的多包管理,给出可落地的工程化方案。
一、工程化的四个维度
| 维度 | 解决的问题 | 工具 | |------|------------|------| | 构建 | 打包、转译、优化 | Vite / webpack / esbuild | | 规范 | 风格统一、防止低级错误 | ESLint / Prettier / Stylelint | | 质量 | 类型、测试、覆盖率 | TypeScript / Vitest / Cypress | | 流程 | 提交规范、自动化 | Husky / lint-staged / CI |
二、构建工具:Vite 现代基线
Vite 基于 esbuild 与原生 ESM,开发冷启动与热更新速度远超 webpack 时代:
// vite.config.ts
export default defineConfig({
plugins: [vue()],
build: {
target: 'es2020',
sourcemap: true,
rollupOptions: {
output: {
manualChunks: { // 手工分包:框架代码独立,长缓存
vue: ['vue', 'vue-router', 'pinia'],
},
},
},
},
})
Vite 实践要点:
- 依赖预构建(esbuild)让启动毫秒级;
- 生产构建自动 Tree-shaking、代码分割;
- 大依赖按需引入或用 CDN 外链。
三、代码规范:从 ESLint 到格式化
3.1 一体的质量配置
// .eslintrc
{
"extends": ["@nuxt/eslint-config", "prettier"],
"rules": {
"no-console": ["warn", { "allow": ["warn", "error"] }],
"vue/multi-word-component-names": "off"
}
}
// .prettierrc
{
"semi": false,
"singleQuote": true,
"printWidth": 100
}
组合策略:ESLint 管逻辑正确性(未使用变量、危险写法),Prettier 管格式统一(引号、缩进、行长),两者职责分离不冲突。
3.2 Git 钩子:提交即门禁
# 安装 husky + lint-staged
npx husky-init
npm i -D lint-staged
// package.json
{
"lint-staged": {
"*.{ts,vue,js}": ["eslint --fix", "prettier --write"],
"*.{json,md}": ["prettier --write"]
}
}
效果:提交时只检查暂存区文件,问题在进仓库前被拦截。
四、提交规范与 CHANGELOG
Conventional Commits + commitlint:
// commitlint.config.js
module.exports = { extends: ['@commitlint/config-conventional'] }
# commit-msg 钩子校验提交信息
npx husky add .husky/commit-msg 'npx commitlint --edit "$1"'
配合 semantic-release 自动版本号与 CHANGELOG:
npx semantic-release
# feat → minor, fix → patch, BREAKING CHANGE → major,自动发版
五、测试体系:单测到 E2E 的分层
// 单元测试:Vitest(组件与组合函数)
import { mount } from '@vue/test-utils'
import { describe, it, expect } from 'vitest'
describe('ArticleCard', () => {
it('渲染标题', () => {
const wrapper = mount(ArticleCard, { props: { title: 'Hello' } })
expect(wrapper.text()).toContain('Hello')
})
})
// E2E:Playwright 覆盖核心用户路径
import { test, expect } from '@playwright/test'
test('登录后发布文章', async ({ page }) => {
await page.goto('/login')
await page.fill('input[name="email"]', 'user@test.com')
await page.fill('input[name="password"]', 'pass')
await page.click('button[type="submit"]')
await expect(page).toHaveURL(/\/dashboard/)
})
分层策略:单测(80% 覆盖核心逻辑)+ 组件测试(关键组件)+ E2E(3~5 条核心路径),避免"全测"的成本爆炸。
六、Monorepo:多项目协同的演进
当出现 admin、site、shared 等多个前端项目共享类型与工具时,Monorepo 用一套依赖管理统一协作:
monorepo/
├── apps/
│ ├── site/ # 用户站点(Nuxt)
│ └── admin/ # 管理后台(Vue)
├── packages/
│ ├── ui/ # 共享组件库
│ ├── utils/ # 共享工具
│ └── types/ # 共享类型定义
├── pnpm-workspace.yaml
└── package.json
# pnpm-workspace.yaml
packages:
- 'apps/*'
- 'packages/*'
6.1 Monorepo 的核心收益
- 依赖统一:共享的 eslint/tsconfig 一处配置全局生效;
- 代码复用:
@repo/ui内部包直接引用,无需发布到 npm; - 原子提交:改 shared 类型,site 与 admin 在同一 PR 验证。
6.2 需要治理的代价
- 构建增量:用 Turborepo / Nx 缓存任务结果,只重建受影响包;
- 版本策略:内部包用
workspace:*协议,统一发版或独立发版二选一; - 约束边界:明确 app 不能 import 兄弟 app 的源码,用 ESLint import 规则强制。
// turbo.json:任务缓存
{
"pipeline": {
"build": { "dependsOn": ["^build"], "outputs": ["dist/**"] },
"lint": {},
"test": {}
}
}
七、落地路线图
- 基线:Vite + TypeScript + ESLint + Prettier + Husky,先立质量门禁;
- 测试:核心模块补单测,关键路径补 E2E,CI 跑
test && lint && typecheck; - 提交规范:commitlint + 自动 CHANGELOG;
- Monorepo(当共享代码出现痛感时):pnpm workspace + Turborepo 增量构建。
结语
前端工程化的目标不是"工具越多越好",而是让质量成为默认值。提交被 lint 拦下低级错误、CI 被测试挡住回归、共享包被类型约束住边界——当这些机制稳定运转,团队成员的精力就能从"互相救火"转向"一起建设"。工程化的投入会以"维护成本下降、交付速度上升"的方式,在每一个迭代周期里复利回报。
读完了?给作者一点支持
评论区加载中…