前端打包优化实战技巧与体积压缩方案

UE丶卫华 前端 阅读 2,600
赞 17 收藏
二维码
手机扫码查看
反馈

打包优化:我到底该用哪个?

最近又在搞构建优化,说实话这玩意儿每次上线前都得折腾一通。这次主要是解决首包太大、加载慢的问题,顺手把几个主流方案拉出来遛了遛。今天想聊的是三个我常用的手段:Code Splitting + 动态 import、Webpack 的 SplitChunksPlugin,还有 Vite 的预构建和按需加载。结论先放这儿:中小型项目我直接上 Vite,大型老项目 Webpack 还是稳一点,但动态拆分这块必须做,不然用户真要骂娘了。

前端打包优化实战技巧与体积压缩方案

谁更灵活?谁更省事?

先说感受:Vite 真的省事。尤其是开发阶段,启动快得离谱,热更新几乎无感。但我这边有个大后台系统是基于 Vue2 + Webpack 4 起家的,迁移到 Vite 成本太高,只能继续在 Webpack 上折腾。

核心问题就一个:怎么让首页加载的 JS 少一点?别一上来就下个 2MB 的 bundle.js,用户网速差一点直接白屏十几秒。

方案一:动态 import + React.lazy(我最常用)

这是我目前线上项目里用得最多的。原理简单:路由级拆分,用户访问哪个页面才加载哪个模块。写法也干净:

const Home = React.lazy(() => import('./pages/Home'));
const About = React.lazy(() => import('./pages/About'));
const Dashboard = React.lazy(() => import('./pages/Dashboard'));

function App() {
  return (
    <Suspense fallback={<div>加载中...</div>}>
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/about" element={<About />} />
        <Route path="/dashboard" element={<Dashboard />} />
      </Routes>
    </Suspense>
  );
}

这里注意我踩过好几次坑:fallback 一定要加,否则会白屏;另外异步组件不能放在条件判断里,比如 if (user) import('admin'),Webpack 解析不了这种动态路径,会报错。

优点很明显:按需加载,打包后每个页面都是独立 chunk,首屏体积立竿见影地小了。而且代码层面很清晰,谁都能看懂。

缺点也有:如果页面太多,HTTP 请求会变多。不过现在 HTTP/2 普及了,这个问题没以前严重。再说了,总比全量加载强吧?

方案二:SplitChunksPlugin —— Webpack 老将出马

这个配置我改了不下十遍,文档看得头都大了。默认其实已经做了 vendor 拆分,但不够细。比如 lodash、moment 这种通用库,我想单独打成一个包,方便长期缓存。

// webpack.config.js
module.exports = {
  optimization: {
    splitChunks: {
      chunks: 'all',
      cacheGroups: {
        vendor: {
          test: /[\/]node_modules[\/]/,
          name: 'vendors',
          chunks: 'all',
        },
        moment: {
          test: /[\/]node_modules[\/](moment|lodash)[\/]/,
          name: 'libs',
          chunks: 'all',
          priority: 10,
        },
      },
    },
  },
};

这个配置亲测有效。打包后生成 vendors.chunk.jslibs.chunk.js,后面那个专门塞第三方工具库,版本不变的话浏览器能一直用缓存。

但这里有个坑:priority 得设高一点,不然会被 vendor 组吃掉。我之前没设,结果 moment 还是在 vendors 里,白配了。

还有一点要注意:如果你用了异步 import,SplitChunks 也会自动处理,不需要额外配置。也就是说它和动态加载不冲突,反而是互补的。

总体来说,SplitChunks 是 Webpack 里最硬核但也最可控的方案。适合那种对构建流程有洁癖、需要精细控制 chunk 分组的团队。

方案三:Vite 的预构建和按需加载 —— 开发体验赢麻了

新项目我基本都上 Vite 了。不是跟风,是真的香。尤其是它的依赖预构建机制,启动快得离谱。

// vite.config.js
export default {
  build: {
    rollupOptions: {
      input: {
        main: 'src/main.tsx',
        about: 'src/pages/About/index.tsx',
      },
    },
    rollupOptions: {
      output: {
        manualChunks: (id) => {
          if (id.includes('node_modules')) {
            return 'vendor';
          }
          if (id.includes('lodash') || id.includes('moment')) {
            return 'utils';
          }
        },
      },
    },
  },
};

Vite 底层是 Rollup,所以打包逻辑更现代一些。它默认就会把 node_modules 里的东西预构建一次,存在 node_modules/.vite 下,下次启动直接用缓存。

实际效果就是:第一次启动稍慢,之后几乎秒开。开发阶段幸福感飙升。

生产构建时,通过 manualChunks 也能实现类似 SplitChunks 的分组策略。语法稍微不一样,但逻辑一致。

不过 Vite 对 IE 支持不好,如果你还要兼容旧浏览器,建议三思。我们公司内部系统去年切 Vite 后,IE 用户投诉一堆,最后还得回 Webpack……

性能对比:差距比我想象的大

我拿同一个项目分别跑了三套方案,本地 build 后用 gzip 压缩算大小:

  • 纯打包(无拆分):main.js 2.1MB → Gzipped 680KB
  • SplitChunks + 静态拆分:main 320KB + vendors 450KB + libs 120KB → 总 Gzipped 590KB,首屏只下 main
  • Vite + manualChunks:main 280KB + vendor 500KB → 总 Gzipped 570KB,首屏同样只下 main

看起来数字差不多?但别忘了 Vite 默认开启 brotli 压缩(可以手动关),而且产物是 ES Module,现代浏览器解析更快。实测首屏可交互时间快了 1.2s 左右。

更别说开发阶段的体验差距了。Webpack 开启 HMR 后修改文件平均热更新 800ms,Vite 基本在 200ms 内完成——这根本不是一个量级的。

我的选型逻辑

看场景,我一般选:

  • 新项目,不用兼容 IE → 直接 Vite,省下的时间够喝两杯咖啡
  • 老项目,Webpack 已有积累 → 上动态 import + SplitChunks,重点拆路由和公共库
  • 超大型项目,多团队协作 → 自定义 chunk 分组,甚至按业务域拆包,比如 admin-vendor、report-utils 这种

还有一个小技巧:我在 Webpack 里会给每个 chunk 加 contenthash,并通过后端接口返回最新的资源列表,避免缓存问题。比如:

// 打包后生成 manifest.json
{
  "main.js": "main.abc123.js",
  "vendors.js": "vendors.def456.js"
}

然后前端请求这个 manifest,再动态插入 script 标签。这样哪怕 CDN 缓存了旧 HTML,也能拿到最新 JS。

这套流程改完后仍有一两个小问题,比如降级失败时的兜底策略还没完善,但无大碍,至少用户不再反馈“点进去转半天”了。

以上是我的对比总结,有不同看法欢迎评论区交流

这个话题其实还能深挖,比如结合 CDN 缓存策略、prefetch/priority 设置、甚至 SSR 下的打包差异。但我已经写了快两千字,脑子有点钝了。先这样吧。

以上是我踩坑后的总结,希望对你有帮助。如果你也在被打包折磨,不妨试试动态 import,改完立马见效。至于 Vite 和 Webpack 的战争,我觉得短期内还会持续,但趋势已经很明显了。

本文章不代表JZTHEME立场,仅为作者个人观点 / 研究心得 / 经验分享,旨在交流探讨,供读者参考。
发表评论

暂无评论