前端工程化这五年:从手搓Webpack到GitLab CI/CD流水线

云端造物者
2025-12-29 13:03
阅读 3901

上个月,我们组刚完成了一个老系统的前端重构。上线那天晚上,我坐在杭州城西的出租屋里,泡了杯速溶咖啡,看着监控面板上稳定的QPS和低于100ms的首屏加载时间,心里居然有点感慨——五年前刚转Java后端那会儿,哪敢想自己有一天要搞前端工程化这套东西。

我是传统制造业出身的开发,前三年写Java写得连IDEA快捷键都刻进DNA了。后来公司搞数字化转型,前后端分离成了政治任务,领导一句“小王你懂点JS,前端这块你先顶一下”,我就这么被推上了火线。一开始用VSCode写React,插件装得比别人多三倍,光是ESLint配置就能调一整天。但谁让杭州这边机会多呢?阿里网易都在招全栈,不逼自己一把,简历怎么敢投?

说真的,前端工程化这事,踩过的坑比我写的SQL语句还多。今天就借着最近这个项目,聊聊这几年在工具链、构建流程、部署策略上的血泪教训。希望能给还在“手写HTML+jQuery”时代挣扎的传统企业兄弟们一点启发。

那个让我半夜三点爬起来改配置的Webpack

事情得从去年双11说起。我们有个内部管理系统,技术栈还是Vue 2 + jQuery混合体,打包慢得像蜗牛爬。每次npm run build,我都能去泡杯茶、刷完朋友圈回来它还在跑。更离谱的是,某次发版后线上白屏,查了半天发现是某个同事不小心把node_modules里的包路径写死了——这系统连基础的CI都没有!

痛定思痛,领导拍板重构。作为“被迫全栈”的代表,我负责搭新框架。首选当然是React(毕竟简历加分项),但更大的挑战在工程化体系。

第一步就是选构建工具。Vite?太新,团队老人接受不了。Webpack?老朋友了,虽然配置复杂但可控。于是我又回到了那个熟悉的噩梦:webpack.config.js。

// 别笑,这是我第一天写的配置
module.exports = {
  entry: './src/index.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'bundle.js'
  },
  module: {
    rules: [
      { test: /\.js$/, use: 'babel-loader' },
      { test: /\.css$/, use: ['style-loader', 'css-loader'] }
    ]
  }
}

结果本地跑得好好的,测试环境一部署,字体图标全乱码。查了两天才发现,生产环境没开file-loader处理静态资源。当时真的想砸电脑。

后来学乖了,搞了个分环境的配置:

// webpack.prod.js
const MiniCssExtractPlugin = require('mini-css-extract-plugin');

module.exports = merge(common, {
  mode: 'production',
  plugins: [
    new MiniCssExtract AssistantPlugin({
      filename: 'css/[name].[contenthash:8].css'
    })
  ],
  module: {
    rules: [
      {
        test: /\.(png|svg|jpg|jpeg|gif)$/i,
        type: 'asset/resource',
        generator: {
          filename: 'images/[name].[hash][ext]'
        }
      }
    ]
  }
});

加上contenthash之后,缓存问题总算解决了。但真正的痛点其实是本地开发体验。每次改一行代码等30秒热更新,程序员的命也是命啊!

Monorepo + Turborepo:拯救多应用项目的银弹?

我们系统其实包含三个子应用:采购、仓储、销售。以前是三个独立仓库,公共组件复制粘贴,改一个bug要提三次PR。测试小姐姐每次都要重复验证,看我的眼神都带着怨念。

受够了!我调研了一圈,决定上Monorepo。但Lerna太重,pnpm workspace又对现有项目改动太大。最后选了Vercel家的Turborepo——轻量、速度快、还能和现有npm scripts无缝集成。

目录结构长这样:

my-enterprise-app/
├── apps/
│   ├── procurement/     # 采购系统
│   ├── warehouse/      # 仓储系统  
│   └── sales/          # 销售系统
├── packages/
│   ├── shared-ui/      # 公共UI组件
│   └── utils/          # 工具函数
├── turbo.json
└── package.json

关键配置在turbo.json:

{
  "pipeline": {
    "build": {
      "dependsOn": ["^build"],
      "outputs": [".next/**", "!.next/cache/**"]
    },
    "dev": {
      "cache": false
    }
  }
}

效果立竿见影:本地启动三个应用,以前要开三个终端,现在一条turbo dev搞定;构建时自动依赖分析,公共组件只编一次。上周五晚上加班联调,隔壁组的老哥路过看到我终端里飞速滚动的日志,直接问:“这啥神器?”

当然也有坑。比如Turborepo默认缓存基于文件内容,但我们的Docker构建上下文变了却不触发重构建。后来加了--force参数才解决。这些细节,文档里可没写。

ESLint + Prettier:让代码风格不再成为撕逼源头

传统企业最怕什么?不是技术债,是代码风格战争。

我们组七个开发,有喜欢单引号的,有坚持双引号的;有人缩进2空格,有人非要4空格。每次CR(Code Review)都能吵半天。有一次,产品经理在群里@我:“页面按钮怎么突然变蓝了?”——结果是因为某人提交时格式化了整个文件,Git diff炸了,漏看了CSS变更。

忍无可忍,我祭出了前端工程化的“和平武器”:ESLint + Prettie r 组合拳。

先统一规则:

// .eslintrc.json
{
  "extends": [
    "react-app",
    "react-app/jest"
  ],
  "rules": {
    "quotes": ["error", "single"],
    "indent": ["error", 2],
    "semi": ["error", "always"]
  }
}

再配Prettier:

// .prettierrc
{
  "semi": true,
  "trailingComma": "es5",
  "singleQuote": true,
  "printWidth": 100,
  "tabWidth": 2
}

最关键的是在VSCode里装了ESLint和Prettier - Code formatter插件,设置保存时自动修复:

// settings.json
{
  "editor.codeActionsOnSave": {
    "source.fixAll.eslint": true
  },
  "prettier.requireConfig": true
}

从此世界清净了。现在新人入职第一天,我只说一句:“装好这两个插件,其他不用管。” 连测试同学都说,最近提测的代码diff清爽多了。

不过也有翻车现场。有次Prettier自动把一行超长的条件判断拆成十行,反而降低了可读性。后来加了// prettier-ignore注释才绕过。工具是死的,人是活的嘛。

自动化部署:从Jenkins到GitLab CI的平滑迁移

前面折腾半天,如果部署还得手动FTP上传,那真是白忙活。

我们公司运维老哥是Jenkins铁粉,但配置界面长得像Windows XP时代的软件。每次改个部署脚本,他都要拉我开会:“小王你看这里是不是要加个参数?”

受不了了!趁公司上GitLab,我说服领导把CI/CD迁过去。GitLab CI的YAML配置简直是对人类友好:

# .gitlab-ci.yml
stages:
  - build
  - test
  - deploy

build_frontend:
  stage: build
  image: node:18
  script:
    - npm ci
    - npm run build
  artifacts:
    paths:
      - dist/
  only:
    - main

deploy_to_staging:
  stage: deploy
  script:
    - echo "Deploying to staging..."
    - rsync -avz --delete dist/ user@staging-server:/var/www/html
  environment:
    name: staging
  when: manual

最爽的是when: manual——测试通过后,点一下按钮才部署到预发环境。再也不用担心半夜自动部署把测试数据搞崩。

但传统企业的网络环境……你懂的。内网GitLab Runner连不上公网npm registry,构建直接挂掉。解决方案?在内网搭个Verdaccio私有源,把常用包缓存起来。虽然又要维护一个服务,但总比求着运维开防火墙强。

性能优化:别让用户等得想卸载APP

工程化不只是工具链,最终要为用户体验服务。我们系统面向一线仓管员,很多还在用千元机。首屏加载超过3秒?人家直接切后台干别的去了。

React本身很快,但bundle太大照样卡。我用Webpack Bundle Analyzer扫了一遍,好家伙,moment.js占了80KB,其实就用了两个format函数!

解决方案三板斧:

  1. 代码分割:路由级懒加载
const WarehouseList = lazy(() => import('./WarehouseList'));
  1. 按需引入:Ant Design改成babel-plugin-import
  2. 替换重型库:moment.js换成dayjs,体积直降90%

效果如何?来看数据:

指标 优化前 优化后 提升
Bundle Size 2.8MB 1.2MB 57% ↓
FCP (首屏) 3.2s 1.1s 65% ↓
TTI (可交互) 4.5s 1.8s 60% ↓

上线后,用户投诉少了八成。连产品经理都发红包感谢我——虽然第二天就提了新需求:“能不能再快点?”

写在最后:工程化不是炫技,是让团队跑得更快

回看这五年,从手写jQuery到搭建完整前端工程体系,最大的感悟是:工程化的价值不在技术多新,而在能否解决实际问题。

在传统企业搞数字化转型,最难的往往不是技术本身,而是改变“能跑就行”的思维惯性。我见过太多团队,花大价钱买低代码平台,结果定制需求一来就抓瞎;也见过用最新框架但连基础CI都没有,天天人工部署。

React也好,Vite也罢,工具永远是为人服务的。现在我的VSCode里依然装着三十多个插件,但最常用的还是那几个:ESLint、Prettier、GitLens。因为它们真正帮我省下了时间,去思考更重要的事。

上周团建,Leader问我:“如果重来一次,还会选这条路吗?” 我想了想,笑着说:“只要不用再手动部署,干啥都行。”

前端工程化这条路,没有银弹,只有不断踩坑、填坑、再踩新坑的过程。但每当看到新来的实习生能快速上手项目,当测试同学夸这次提测质量高,当用户反馈“系统变快了”——那一刻就觉得,所有的折腾都值得。

这就是我的代码人生:在传统与现代的夹缝中,用一行行配置、一个个脚本,为老系统注入新生命。或许不够酷炫,但足够踏实。

(完)

后记:本文所有方案均已在生产环境稳定运行半年以上。如果你也在传统企业搞前端,欢迎交流——邮箱在GitHub主页,备注“工程化”优先通过。另外,求推荐杭州靠谱的前端岗位,坐标城西,可remote。

评论 0

最热最新
暂无评论
云端造物者Lv.1
0
影响力
0
文章
0
粉丝