.github/workflows/ci.yml

字段又改名了
2025-06-26 01:09
阅读 605

从零到一搭建前端工程化体系:一次真实的架构演进实践

从零到一搭建前端工程化体系:一次真实的架构演进实践

去年我加入了一个中型互联网公司的前端团队,负责重构一个长期由多个小团队分散开发、技术栈混杂的内部管理后台项目。这个项目在当时已经运行了近四年,累计迭代过几十个版本,涉及 Vue 2、React、jQuery 等多种框架混用,构建流程混乱、代码质量参差不齐、部署方式落后等问题日益突出,最终导致新功能上线效率低下,线上故障频发。

作为接手这个项目的负责人之一,我们面临的一个核心问题是——如何让这个“历史包袱”重新焕发活力,提升团队协作效率和工程质量?带着这个问题,我们开始了为期三个月的前端工程化改造之路。

问题描述:当项目规模失控时,混乱无处不在

刚接手项目那会儿,我做的第一件事就是查看项目的构建配置文件。不出所料,package.json 里堆了一大堆 devDependencies,其中不少是重复依赖或者已经废弃的库;.eslintrc 的规则混乱且未统一应用;构建脚本散落在多个地方,甚至有些页面还使用原始的 HTML + JS 脚本引入方式,完全谈不上模块化或组件化。

更严重的问题在于协作层面:

  • 每个小组有自己的代码风格,每次合并 PR 都像一场灾难
  • 没有统一的 CI/CD 流程,上线靠人肉执行 shell 脚本
  • 基础设施老旧,很多页面只能兼容 IE11(但没人真的测试)
  • 构建时间越来越慢,本地 build 动不动就卡死机器
  • 技术债务严重,没有人愿意动某些“祖传”模块

这些问题不仅影响开发效率,也带来了用户端明显的体验问题:页面加载速度变慢、交互卡顿,甚至部分页面在高分辨率下显示异常。

解决方案:打造现代化前端工程体系

我们的目标非常明确:通过工程化手段,建立标准化、可维护、可持续迭代的技术体系。 我们将其拆解为四个维度来推进:

  1. 工具链统一(ESLint/Prettier/Webpack/Vite)
  2. 开发规范落地(编码风格+模块设计+组件复用)
  3. 自动化构建与部署(CI/CD + Docker)
  4. 性能优化与监控(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"]
  }
}

现代网页界面设计示例-1

同时,我们还做了几个关键决策:

  • 强制启用 TypeScript,逐步迁移老代码
  • 统一使用 Prettier 进行格式化,Git Hook 自动修复
  • 使用 Husky + lint-staged 保证提交前代码质量
  • 接入 Jest + Testing Library 实现单元测试覆盖率要求

持续集成与部署:告别“手动上线”

我们选择了 GitHub Actions 作为 CI 平台,配合 GitLab CI 用于部署阶段。构建流程分为以下几个阶段:

  1. Pull Request 触发 Lint + Unit Test
  2. Merge 后触发 Build + E2E Test(Cypress)
  3. 成功后打包 Docker 镜像并推送到私有 Registry
  4. 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天

更重要的是,整个团队在工程化意识上有了显著提升。大家开始主动参与代码评审、写单测、关注性能指标。这种文化上的转变比技术本身更加珍贵。


给读者的建议:别只盯着“最佳实践”,更要关注“合适实践”

最后我想给准备做工程化转型的同学几点建议:

  1. 不要追求“完美”工具链,先解决最痛的问题。 比如如果你团队最头疼的是代码风格混乱,那就优先上 ESLint + Prettier;如果部署经常出错,那就先搞定 CI/CD。

  2. 渐进式改造,避免重写陷阱。 我见过太多“我要把整个项目用 React 重写”的豪言壮语最后都变成了噩梦。正确的做法是“边用边改”,逐步替换成模块、升级工具链。

  3. 建立标准文档并强制落地。 没有文档的“约定”很快就会失效。你可以用 Confluence、Notion 或者 Wiki,关键是让大家知道去哪查标准、怎么遵守。

  4. 工具只是手段,人才是根本。 再好的配置也无法替代工程师自身的工程素养。多做一些内部分享、Code Review、Pair Programming,让工程文化真正落地。

  5. 性能优化要结合真实数据,不是跑分游戏。 有时候你花一天优化出来的 1% 加载提速,可能不如调整个图片大小来的实际。学会看 Network 面板、Performance 时间线,比盲目追新技术更有意义。


结尾:工程化不是终点,而是一种思维习惯

如今再回头看这段工程化历程,我觉得它带给我的最大改变,不是学了几门新工具、写了多少 CI 脚本,而是让我更理解了「软件工程」的本质:它是关于组织人、协同做事、持续改进的过程。

前端工程化并不是一套固定公式,也不是非得所有项目都标配 Webpack + Eslint + Docker。它的本质是:让代码更容易写、更容易改、更容易维护。

希望这篇文章对你正在经历的工程化旅程有所启发。也欢迎留言交流你的实战经验和踩过的坑,咱们一起进步!

评论 0

最热最新
暂无评论
匿名用户Lv.1
0
影响力
0
文章
0
粉丝