前端工程化实战:从本地开发到线上部署的完整链路

Dev工程师
2025-12-22 06:27
阅读 1173

上周五晚上十一点,我还在公司改一个 React 项目的构建配置。产品经理在群里@我说“明天演示要用新功能”,而我们的 CI/CD 流水线又莫名其妙挂了。那一刻我真的想砸电脑——但转念一想,这不就是我们三线城市小厂技术负责人的日常吗?

我是老张,在这家做本地生活服务的互联网公司带前端团队快两年了。说“带”其实有点夸张,毕竟整个前端组就仨人,其中一个还是实习生。但麻雀虽小,五脏俱全,该踩的坑一个没少。去年双11期间,我们因为构建缓存没清理干净,导致上线后首页白屏,用户投诉电话直接打到了CEO那儿。从那以后,我就下定决心把前端工程化搞明白。

最近我也在偷偷学AI,不是为了转行(虽然确实有点心动),主要是看到GitHub上那些AI辅助编程的工具越来越香,想着能不能用到我们流程里。不过今天先不聊AI,聊聊更接地气的——怎么让前端开发不那么“玄学”。

工具链:别再手动 npm install 了

很多新人入职第一天,都会被我们复杂的依赖安装流程劝退。以前我们也像大多数小团队一样,直接 npm install 完事。直到有一次,一个同事用了新版 Node.js,结果本地跑得好好的代码,一推到测试环境就报错:“Cannot find module 'react-dom/server'”。

后来我们统一了几个关键点:

  1. 锁定 Node 版本:用 .nvmrc 文件指定版本
  2. 包管理器统一:强制使用 pnpm(比 yarn 快,比 npm 省空间)
  3. 依赖分层:区分 devDependencies、dependencies 和 peerDependencies
# .nvmrc
18.17.0

# package.json 关键配置
{
  "engines": {
    "node": ">=18.0.0"
  },
  "scripts": {
    "preinstall": "node ./scripts/check-engine.js"
  }
}

那个 check-engine.js 脚本很简单,就是检查当前 Node 版本是否匹配。别小看这一步,自从加了它,新人入职第一天就能跑起来项目,再也不用在群里问“为啥我本地报错了”。

说到新人,前两天面试了一个应届生,简历上写着“熟悉前端工程化”。我让他现场解释一下 Webpack 和 Vite 的区别,结果他支支吾吾说不清。其实我不指望应届生懂多深,但至少得知道怎么配个 alias 吧?求职市场卷成这样,连基础工具链都不了解,真的很难拿到 offer。

构建优化:React 项目也能秒开

我们的主应用是 React + TypeScript 写的,页面数不多但功能杂。最开始用 Create React App,简单是简单,但打包速度慢得像蜗牛。随着组件越来越多,每次 npm run build 都要等两分钟,开发体验极差。

后来我们迁移到 Vite,配合一些自定义配置:

// vite.config.ts
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'

export default defineConfig({
  plugins: [react()],
  resolve: {
    alias: {
      '@': path.resolve(__dirname, './src'),
      // 避免相对路径地狱
    }
  },
  build: {
    sourcemap: true,
    rollupOptions: {
      output: {
        manualChunks(id) {
          // 将第三方库单独打包
          if (id.includes('node_modules')) {
            return id.toString().split('node_modules/')[1].split('/')[0].toString();
          }
        }
      }
    }
  }
})

这个 manualChunks 配置特别有用。之前所有第三方库都打在一个 vendor.js 里,有 2MB 大小。现在按包名拆分,首屏加载时间从 4s 降到 1.2s。而且缓存策略也更好做了——业务代码经常变,但 lodash、moment 这些工具库很少更新。

当然,Vite 也不是万能的。有次我们用了一个比较冷门的 UI 库,Vite 的 HMR(热更新)直接失效,改一行代码就要全量刷新。查了半天才发现是那个库用了动态 require。这种时候只能默默加个 /* @vite-ignore */ 注释,然后祈祷别再遇到类似问题。

自动化测试:别让爬虫毁了你的数据

说到测试,很多人觉得前端不需要。但我们在做 SEO 优化时吃过亏。为了让搜索引擎能抓取内容,我们上了 SSR(服务端渲染)。结果某天发现,百度爬虫访问时页面全是 loading 状态。

原因很简单:SSR 渲染时,有些数据请求依赖客户端的 cookie 或 localStorage,而爬虫根本没有这些。后来我们加了一套自动化测试:

  • 用 Puppeteer 模拟爬虫访问
  • 检查关键内容是否渲染出来
  • 监控首屏加载性能
// tests/crawler.test.js
test('首页应包含核心内容,即使无用户上下文', async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  
  // 模拟爬虫 UA
  await page.setUserAgent('Mozilla/5.0 (compatible; Baiduspider/2.0; +http://www.baidu.com/search/spider.html)');
  
  await page.goto('http://localhost:3000');
  const content = await page.$eval('.main-content', el => el.textContent);
  
  expect(content).toContain('本地生活服务');
  await browser.close();
});

这套测试集成到 CI 流程里,每次 PR 都会跑一遍。虽然增加了几分钟构建时间,但避免了线上事故,值了。

其实做这些测试还有一个私心:我们团队最近在招人,我想在 GitHub 仓库里展示一些工程化实践,让求职者看到我们不是那种“写页面就行”的作坊式团队。毕竟现在好点的前端工程师,都会看公司技术栈和开发流程。

部署流程:告别手动 FTP

两年前,我们的部署流程是这样的:开发完功能 → 手动打包 → 用 FileZilla 上传到服务器 → SSH 登录重启服务。听起来是不是很原始?但这就是很多小公司的现状。

后来我们搭了 GitLab CI,流程变成了:

  1. 提交代码到 feature 分支
  2. 创建 MR(Merge Request)
  3. 自动运行 lint + test
  4. 合并到 develop 分支后,自动部署到测试环境
  5. QA 通过后,合并到 main 分支,自动部署到生产环境

关键配置如下:

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

build_app:
  stage: build
  script:
    - pnpm install
    - pnpm run build
  artifacts:
    paths:
      - dist/

test_crawler:
  stage: test
  script:
    - pnpm run test:crawler

deploy_production:
  stage: deploy
  script:
    - rsync -avz --delete dist/ user@prod-server:/var/www/html/
    - ssh user@prod-server "pm2 reload app"
  only:
    - main

这里有个小技巧:我们用 rsync 而不是直接 scp 整个目录。它只会传输变化的文件,速度快很多。而且 --delete 参数能确保删除已移除的文件,避免旧文件残留。

不过最让我自豪的不是这些自动化脚本,而是我们建立的回滚机制。每次部署前,CI 会自动备份当前版本。万一线上出问题,运维同事(其实是我自己)可以在 30 秒内回滚到上一个稳定版本。去年春节大促前,我们就靠这个救了场——当时新版本有个内存泄漏,流量一上来服务器直接 OOM。回滚后,我一边喝着咖啡一边修 Bug,心态稳得一批。

总结:工程化不是炫技,是生存必需

回头看这两年,前端工程化给我们带来的最大改变不是技术上的,而是心态上的。以前总觉得“小公司没必要搞这么复杂”,结果每次加班都是因为流程混乱。现在虽然前期投入多,但长期来看省下了大量救火时间。

最近我还尝试把 AI 工具集成到流程里。比如用 GitHub Copilot 写单元测试,或者用一些 AI linter 检查代码风格。效果嘛……只能说辅助作用,不能太依赖。但至少让重复工作少了一些,可以多花点时间思考架构问题。

如果你也在一个小团队,我的建议是:不要追求一步到位,但一定要有演进意识。今天解决一个问题,明天优化一个环节,积少成多。毕竟我们不是 FAANG,资源有限,但也不能躺平。

对了,上周那个周五晚上的构建问题,最后发现是 GitHub Actions 的缓存没清理。清掉缓存重新跑,一分钟搞定。我当时就想,要是早点把这套流程文档写好,新人也不用半夜被叫起来排查问题了。

所以写下这篇文章,既是总结,也是给团队留个文档。希望下次招人的时候,能让求职者看到:虽然是三线城市的小公司,但我们认真对待每一行代码。

评论 0

最热最新
暂无评论
Dev工程师Lv.1
0
影响力
0
文章
0
粉丝