请写一篇关于【前端工程化最佳实践:从工具链到部署流程】的技术文章。写作风格要求:踩坑经验分享。文章需要涵盖以下关键词:Sora,文心一言,Ollama,React。

张秀兰△
2026-07-23 05:53
阅读 421

凌晨三点,我在浦东的出租屋里重构了整个前端工程

上周五晚上十一点半,女朋友已经睡了。我蹑手蹑脚从床上爬起来,轻手轻脚地走到客厅那张1米2的小书桌前,打开MacBook。屏幕的蓝光映在脸上,我灌了半罐已经没气的可乐,盯着屏幕上那坨跑了8分钟的CI/CD流水线,脑子里只有一个念头:

"这破工程化,老子今天非得给它整明白不可。"

先自我介绍一下。我,坐标上海浦东,某中型互联网公司前端开发,工龄四年半。去年十月,在经历了大概二十多次相亲——对,二十多次,我妈给我安排的,有护士、有老师、有银行柜员,甚至还有一个在静安寺卖水晶的——终于,在一个朋友组织的剧本杀局上,遇到了我现在的媳妇儿,小雅。

小雅是做UI设计的,我们在浦东张江附近合租了一个两室一厅,房租3500,押一付三。说实话,刚搬进来的时候,我月薪15k,交完房租再扣掉五险一金,每个月到手也就一万出头。当时真的很焦虑,看着浦东的房价,再看看自己的银行余额,差点想放弃前端这行回老家考公。

但小雅跟我说了一句话,我记到现在:"你先把眼前的事做好,别总想那些有的没的。"

就是这句话,让我决定死磕前端工程化。因为当时我们组的项目,工程化简直是一坨屎。


那坨屎一样的旧工程

我们组的项目是一个ToB的后台管理系统,技术栈是React 16 + Webpack 4 + 一堆乱七八糟的loader和plugin。说实话,这配置还是三年前一个已经离职的大哥留下来的,没人敢动,也没人会动。

具体有多烂呢?我给你说几个数字:

  • 本地冷启动:4分37秒。对,你没看错,改一行代码,等四分多钟才能看到效果。
  • 生产环境构建:8分12秒。每次发版,我都得去楼下便利店买杯咖啡等着。
  • 打包产物体积:首屏JS 2.3MB。用户打开页面,得转圈转个五六秒。
  • CI/CD流水线:从代码提交到部署上线,22分钟

我们组一共四个前端,每天光是等构建、等部署,就能浪费掉差不多一个小时。组长老王每次周会都骂:"你们能不能快点?业务方催得紧!"但没人敢接话,因为大家都知道,这锅不是前端的,是工程化的。

转折发生在今年三月份。公司接了个大项目,要给某金融机构做一套数据可视化平台。老板亲自开会,说这个项目要用最新的技术栈,要"体现公司的技术实力"。

老王把任务分下来,让我负责前端工程化的搭建。

当时我心里是崩溃的。我之前的经验,说白了就是"能跑就行"。什么Monorepo、什么微前端、什么模块化构建,我都是面试的时候背过概念,实操基本为零。

但我没退路。小雅那会儿刚换了工作,月薪从8k涨到了12k,我们正准备攒钱下半年结婚。我如果在这个项目上拉胯,别说涨薪了,能不能保住现在的位置都两说。


踩坑之路:从Vite到Turborepo

第一个坑:构建工具的选择

最开始我想的是继续用Webpack 5,毕竟团队都熟悉。但实测下来,冷启动还是要两分多钟,根本达不到老板要求的"秒级启动"。

后来我试了Vite。说实话,第一次跑起来的时候我惊了——冷启动1.2秒。我当时在工位上差点喊出来,隔壁工位的小李以为我疯了。

但Vite也不是万能的。我们项目里有一些老模块依赖CommonJS,Vite对CJS的支持一直是个坑。我花了整整三天时间,写了一个自定义插件去转换这些模块,头发都掉了一把。

// vite.config.js 中处理 CJS 兼容的踩坑代码
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import commonjs from 'vite-plugin-commonjs'

export default defineConfig({
  plugins: [
    react(),
    commonjs({
      // 这里踩了个大坑:有些包用了动态 require
      // 必须手动指定 include
      include: [/node_modules\/(lodash|moment)/]
    })
  ],
  build: {
    rollupOptions: {
      output: {
        manualChunks: {
          vendor: ['react', 'react-dom'],
          antd: ['antd', '@ant-design/icons'],
          echarts: ['echarts']
        }
      }
    }
  }
})

第二个坑:Monorepo的诱惑与代价

项目越做越大,组件库、工具函数库、业务代码全堆在一个仓库里,代码review的时候简直是灾难。我提议上Monorepo,用Turborepo来管理。

理想很丰满,现实很骨感。

第一个问题:Turborepo的缓存机制。理论上它应该能缓存没改动的包的构建结果,但实际上,我们有一个共享的utils包,每次改一行代码,所有依赖它的业务包都要重新构建。我研究了好久,才发现是tsconfig的paths配置没对齐,导致Turborepo认为依赖关系变了。

第二个问题:CI/CD流水线的适配。我们之前的Jenkins配置是针对单仓库的,改成Monorepo之后,得重新写pipeline。我花了两个周末,才把增量构建的逻辑跑通。

# turbo.json 踩坑记录
{
  "$schema": "https://turbo.build/schema.json",
  "pipeline": {
    "build": {
      "dependsOn": ["^build"],
      "outputs": ["dist/**"]
      // 坑:这里如果不配 outputs,
      // Turborepo 每次都会重新构建
    },
    "lint": {
      "outputs": []
    },
    "test": {
      "dependsOn": ["build"],
      "outputs": ["coverage/**"]
    }
  }
}

第三个坑:部署流程的噩梦

构建搞定了,部署又是一坨屎。

我们之前的部署流程是这样的:前端打包 → 手动把dist目录scp到服务器 → 手动重启Nginx。对,手动。每次发版,我都得SSH到服务器上敲命令,有一次手抖把生产环境的配置删了,整个系统挂了四十分钟,我被老王骂得狗血淋头。

我花了两周时间,搭了一套完整的CI/CD流程:

  1. 代码提交 → 触发GitLab CI
  2. 自动化检查 → ESLint + Prettier + Stylelint + 单元测试
  3. 自动化构建 → Turborepo增量构建
  4. Docker镜像打包 → 推到内部镜像仓库
  5. K8s滚动部署 → 自动灰度发布

这套流程搭好之后,发版时间从22分钟降到了6分钟,而且全程自动化,再也不用手动SSH了。

# 多阶段构建 Dockerfile,这个也踩了坑
# 坑:Node 镜像太大,生产环境用 alpine 版本
FROM node:18-alpine AS builder
WORKDIR /app
COPY package.json pnpm-lock.yaml ./
RUN corepack enable && pnpm install --frozen-lockfile
COPY . .
RUN pnpm turbo build --filter=@app/web

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;"]

AI工具的加持:那些帮我续命的夜晚

说实话,整个工程化重构的过程中,如果不是有AI工具帮忙,我可能早就放弃了。

那段时间,我几乎每天都在用文心一言。不是那种"帮我写个函数"的用法,而是把它当成一个技术顾问。比如我在研究Turborepo缓存机制的时候,直接把报错日志和配置文件贴给它,让它帮我分析原因。有一次凌晨两点,我死活搞不定一个Webpack到Vite迁移的兼容问题,文心一言给了我一个很冷门的思路——用@originjs/vite-plugin-commonjs替代我手写的那个插件,问题迎刃而解。

后来Sora火了之后,我也试着用它来做一些项目演示视频的生成。你别说,给业务方汇报的时候,放一段Sora生成的产品概念视频,比干巴巴地讲PPT有用多了。老板看完直接说:"这个效果不错,下个月预算给你加20%。"

至于Ollama,这是我最近才开始玩的。我在本地跑了一个CodeLlama模型,主要是用来做代码review的辅助。虽然效果比不上云端的大模型,但胜在免费、隐私安全,而且不用联网。有时候半夜改bug,不想把公司代码传到外部API,Ollama就派上用场了。

当然,最核心的业务代码,还是得靠React本身的基本功。工程化再好,代码写得烂也是白搭。我花了大量时间重构组件,把之前那些两千行的巨型组件拆成粒度更小的hooks和组件,配合React 18的Concurrent Features,首屏渲染性能直接提升了一倍。

// React 18 性能优化的一个实际例子
import { Suspense, useTransition, useState } from 'react'

function Dashboard() {
  const [isPending, startTransition] = useTransition()
  const [data, setData] = useState(null)

  const handleFilterChange = (filter) => {
    // 用 startTransition 包裹非紧急更新
    // 避免阻塞用户输入
    startTransition(() => {
      setData(fetchFilteredData(filter))
    })
  }

  return (
    <div>
      <FilterBar onChange={handleFilterChange} />
      {isPending ? <Skeleton /> : (
        <Suspense fallback={<Loading />}>
          <DataGrid data={data} />
        </Suspense>
      )}
    </div>
  )
}

成果与收获

折腾了大概四个月,到今年七月份,整个前端工程化体系终于稳定下来了。给大伙看看数据对比:

指标 改造前 改造后
本地冷启动 4分37秒 1.2秒
生产构建 8分12秒 2分40秒
首屏JS体积 2.3MB 487KB
CI/CD全流程 22分钟 6分钟
发版事故率 每月1-2次 连续3个月零事故

老王在季度总结会上专门提了一嘴,说我们组的工程化水平"达到了公司前列"。年底调薪,我从15k涨到了22k。虽然在上海这点钱也就那样,但至少,我和小雅的婚礼有着落了。


一些真心话

写了这么多,其实最想说的是:前端工程化不是什么高大上的东西,它本质上就是让你和你的团队少加班、少背锅、少掉头发的工具。

不要为了用新工具而用新工具。Vite很好,但如果你的项目大量依赖CJS,迁移成本可能比你想象的高得多。Monorepo很好,但如果你的团队只有两三个人,可能就是过度设计了。

还有,一定要善用AI工具。文心一言、Ollama这些,不是要替代你,而是帮你加速学习曲线。我当年要是有人告诉我Turborepo的缓存坑在哪,我能少熬多少个夜?

最后,也是最重要的——别把生活搞丢了

重构工程化那四个月,我几乎每天都是最后一个走公司的。小雅虽然没说什么,但我知道她有意见。有一次周末,她拉我去世纪公园散步,我脑子里还在想Docker配置的事,她直接生气了:"你到底是在跟我谈恋爱,还是在跟电脑谈恋爱?"

那句话把我骂醒了。技术固然重要,但生活才是目的。后来我给自己定了规矩:晚上十一点之后不碰代码,周末至少抽一天完全不工作。

现在回想起来,那段日子虽然苦,但真的很充实。从一个"能跑就行"的切图仔,到现在能独立搭建一整套前端工程化体系,这个过程让我成长了很多。

如果你也在经历类似的困境,别怕,慢慢来。把问题拆小,一个一个解决。实在不行,就出去走走,吹吹浦东的风,看看陆家嘴的灯。

日子总会好起来的。就像我和小雅,从合租的两室一厅,到上个月刚签了一套小两居的购房合同——虽然还在外环外,但好歹是在上海扎根了。

好了,不说了,小雅喊我吃饭了。今天是周五,她做了红烧肉。

以上,共勉。


写于2024年某个周五的傍晚,浦东

评论 0

最热最新
暂无评论
张秀兰△Lv.1
0
影响力
0
文章
0
粉丝