.github/workflows/ci.yml
从零到一搭建前端工程化体系:一次真实的架构演进实践

去年我加入了一个中型互联网公司的前端团队,负责重构一个长期由多个小团队分散开发、技术栈混杂的内部管理后台项目。这个项目在当时已经运行了近四年,累计迭代过几十个版本,涉及 Vue 2、React、jQuery 等多种框架混用,构建流程混乱、代码质量参差不齐、部署方式落后等问题日益突出,最终导致新功能上线效率低下,线上故障频发。
作为接手这个项目的负责人之一,我们面临的一个核心问题是——如何让这个“历史包袱”重新焕发活力,提升团队协作效率和工程质量?带着这个问题,我们开始了为期三个月的前端工程化改造之路。
问题描述:当项目规模失控时,混乱无处不在
刚接手项目那会儿,我做的第一件事就是查看项目的构建配置文件。不出所料,package.json 里堆了一大堆 devDependencies,其中不少是重复依赖或者已经废弃的库;.eslintrc 的规则混乱且未统一应用;构建脚本散落在多个地方,甚至有些页面还使用原始的 HTML + JS 脚本引入方式,完全谈不上模块化或组件化。
更严重的问题在于协作层面:
- 每个小组有自己的代码风格,每次合并 PR 都像一场灾难
- 没有统一的 CI/CD 流程,上线靠人肉执行 shell 脚本
- 基础设施老旧,很多页面只能兼容 IE11(但没人真的测试)
- 构建时间越来越慢,本地 build 动不动就卡死机器
- 技术债务严重,没有人愿意动某些“祖传”模块
这些问题不仅影响开发效率,也带来了用户端明显的体验问题:页面加载速度变慢、交互卡顿,甚至部分页面在高分辨率下显示异常。
解决方案:打造现代化前端工程体系
我们的目标非常明确:通过工程化手段,建立标准化、可维护、可持续迭代的技术体系。 我们将其拆解为四个维度来推进:
- 工具链统一(ESLint/Prettier/Webpack/Vite)
- 开发规范落地(编码风格+模块设计+组件复用)
- 自动化构建与部署(CI/CD + Docker)
- 性能优化与监控(Lighthouse/Audit)
下面我重点讲讲我们在每个环节的具体做法和思考。
工具链重构:从混乱到统一
起初,项目中多个子系统各自为政,有的用 Webpack,有的用 Rollup,还有用 Parcel 的。为了统一技术栈,我们最终选择以 Vite 为主力构建工具,并逐步替换旧项目中的构建流程。
Vite 有几个关键优势吸引了我们:
- 开箱即用的 TypeScript 和 JSX 支持
- 快速热更新(基于原生 ES 模块)
- 插件生态丰富,支持按需加载、自动导入等功能
我们首先新建了一个基础模板仓库 frontend-boilerplate,里面包含了统一的 ESLint 规则、TypeScript 配置、Prettier 设置等,并通过 npm package 发布给各子项目引用。这样,不管是谁负责哪个模块,都能保证基础的开发体验一致。
// package.json
{
"devDependencies": {
"eslint": "^8.0.0",
"prettier": "^2.6.2",
"@my-company/eslint-config": "^1.0.0",
"vite": "^3.0.0",
"typescript": "^4.7.4"
},
"eslintConfig": {
"extends": ["@my-company"]
}
}

同时,我们还做了几个关键决策:
- 强制启用 TypeScript,逐步迁移老代码
- 统一使用 Prettier 进行格式化,Git Hook 自动修复
- 使用 Husky + lint-staged 保证提交前代码质量
- 接入 Jest + Testing Library 实现单元测试覆盖率要求
持续集成与部署:告别“手动上线”
我们选择了 GitHub Actions 作为 CI 平台,配合 GitLab CI 用于部署阶段。构建流程分为以下几个阶段:
- Pull Request 触发 Lint + Unit Test
- Merge 后触发 Build + E2E Test(Cypress)
- 成功后打包 Docker 镜像并推送到私有 Registry
- K8s 上自动拉取镜像并滚动发布
这极大提升了部署效率,减少了人为失误的风险。
我们的一段典型 CI 配置如下:
name: Frontend CI
on:
push:
branches:
- main
pull_request:
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install Dependencies
run: npm install
- name: Run ESLint
run: npm run lint
build:
needs: lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '16'
- name: Cache dependencies
uses: actions/cache@v3
with:
path: node_modules
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
- name: Build
run: npm run build
性能优化:不只是快一点那么简单
我们用了 Lighthouse 对改版前后的页面进行打分,发现首页的 Performance 分数从 57 提升到了 92,主要得益于以下几点:
- 使用 SplitChunks 分离公共包
- 启用 Gzip 压缩
- 图片懒加载 + WebP 格式转换
- 静态资源 CDN 托管
另外我们也引入了 Sentry 来收集前端错误日志,以及埋点统计页面加载行为,持续监控用户体验。
踩坑经验分享:那些深夜调试的故事
在重构过程中我们踩了不少坑,这里挑几个印象比较深的来讲讲。
坑点 1:Vue 2 兼容 Vue 3 插件失败
项目中有一部分旧模块还在使用 Vue 2,但我们希望使用新的 Vue 3 生态下的 UI 库(比如 Naive UI)。一开始我们直接安装并尝试使用,结果页面白屏,控制台报错提示找不到响应式属性。后来发现这是由于 Vue 2 的 Composition API 无法识别一些 Vue 3 特性的原因。
解决方案:
- 使用 vue-demi 来抽象出一个兼容层
- 将插件调用封装成兼容性包装器,根据运行环境做判断
坑点 2:Docker 构建缓存失效频繁
刚开始上线 CI/CD 时,我们经常遇到构建缓慢的问题,排查发现每次 CI 都要重新安装依赖,即使 package.json 没变化。
解决方案:
- 使用 GitHub Actions 的缓存机制缓存 node_modules
- 使用
.npmrc配置 registry 地址加快依赖下载 - 升级 NPM 包版本一致性策略(使用 lock file)
坑点 3:IE11 兼容引发的 polyfill 血案
虽然项目计划是放弃对 IE11 的支持,但由于产品方坚持部分客户仍在使用,我们不得不临时启用兼容策略。一开始我们启用了默认的 Babel 配置,结果出现 Promise undefined 的报错。
解决方案:
- 在 vite.config.ts 中显式添加 core-js polyfill:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import legacy from '@vitejs/plugin-legacy'
export default defineConfig({
plugins: [
vue(),
legacy({
targets: ['ie >= 11'],
additionalLegacyPolyfills: ['regenerator-runtime/runtime']
})
]
})
不过,这也提醒我们尽早推动业务侧淘汰老旧浏览器。
成果与收获:技术债也能变成生产力红利
重构完成后,我们得到了以下成果:
| 指标 | 改造前 | 改造后 |
|---|---|---|
| 首页 Lighthouse 性能评分 | 57 | 92 |
| 构建耗时(平均) | 6分钟 | 1分30秒 |
| 部署频率 | 每周1次 | 每天多次 |
| 错误上报数量 | 日均30+ | 下降至个位 |
| 新成员上手周期 | 1~2周 | <3天 |
更重要的是,整个团队在工程化意识上有了显著提升。大家开始主动参与代码评审、写单测、关注性能指标。这种文化上的转变比技术本身更加珍贵。
给读者的建议:别只盯着“最佳实践”,更要关注“合适实践”
最后我想给准备做工程化转型的同学几点建议:
不要追求“完美”工具链,先解决最痛的问题。 比如如果你团队最头疼的是代码风格混乱,那就优先上 ESLint + Prettier;如果部署经常出错,那就先搞定 CI/CD。
渐进式改造,避免重写陷阱。 我见过太多“我要把整个项目用 React 重写”的豪言壮语最后都变成了噩梦。正确的做法是“边用边改”,逐步替换成模块、升级工具链。
建立标准文档并强制落地。 没有文档的“约定”很快就会失效。你可以用 Confluence、Notion 或者 Wiki,关键是让大家知道去哪查标准、怎么遵守。
工具只是手段,人才是根本。 再好的配置也无法替代工程师自身的工程素养。多做一些内部分享、Code Review、Pair Programming,让工程文化真正落地。
性能优化要结合真实数据,不是跑分游戏。 有时候你花一天优化出来的 1% 加载提速,可能不如调整个图片大小来的实际。学会看 Network 面板、Performance 时间线,比盲目追新技术更有意义。
结尾:工程化不是终点,而是一种思维习惯
如今再回头看这段工程化历程,我觉得它带给我的最大改变,不是学了几门新工具、写了多少 CI 脚本,而是让我更理解了「软件工程」的本质:它是关于组织人、协同做事、持续改进的过程。
前端工程化并不是一套固定公式,也不是非得所有项目都标配 Webpack + Eslint + Docker。它的本质是:让代码更容易写、更容易改、更容易维护。
希望这篇文章对你正在经历的工程化旅程有所启发。也欢迎留言交流你的实战经验和踩过的坑,咱们一起进步!

评论 0