人人都会AI编程

21.4 构建优化策略:体积优化、速度优化、分包策略

更新时间:2026-07-11

构建产物的体积和构建过程的速度,直接影响用户体验和开发效率。一个庞大的 JavaScript 文件会让首屏加载缓慢,一次漫长的构建会让开发等待成本陡增。好的构建配置不是“一次性配完就不管”,而是随着项目体量增长持续调优的过程。本节将从体积优化速度优化分包策略三个维度,给出可直接落地的优化方案。


21.4.1 体积优化:让产出的代码尽可能小

1. Tree Shaking(摇树优化)

Tree Shaking 的作用是剔除未使用的代码(dead code),前提是代码使用了 ES Modules 的静态导入/导出语法。Webpack 在 mode: 'production' 下默认开启,Vite 底层使用 Rollup 也是天然支持。为了让它发挥效果,开发时需要注意:

  • 避免使用 export default 导出大对象,尽量使用命名导出,让打包工具更精准地识别引用关系。
  • package.json 中通过 "sideEffects": false 标记整个库无副作用;若有对全局样式、polyfill 等依赖,可用数组准确声明副作用文件,防止被错误移除。
  • 对于按需引入的组件库(如 lodash 代替 lodash-es),务必使用 ESM 版本,否则无法 tree-shake。

2. 代码压缩与混淆

生产环境下,Webpack 和 Vite 都会默认启用压缩:

  • JavaScript 使用 Terser(Webpack)或 esbuild(Vite),它们会移除空格、注释、缩短变量名、简化表达式。
  • CSS 压缩可使用 cssnano,HTML 压缩可用 html-minifier
  • 对于现代浏览器,可以输出 ES6+ 代码而无需过度降级到 ES5,避免 Babel 产生的大量辅助代码。例如使用 @babel/preset-envtargets 配置,分别生成 module 产物和 legacy 产物,结合 <script type="module"> 实现差异化加载。
// babel.config.js 示例:按浏览器目标产出更精简的代码
module.exports = {
  presets: [
    ['@babel/preset-env', {
      targets: {
        esmodules: true  // 只面向支持 ES Modules 的现代浏览器
      }
    }]
  ]
};

3. CSS 优化

  • 在使用 Tailwind 等原子化 CSS 框架时,通过内容扫描剔除未使用的样式类(JIT 模式),可大幅减少 CSS 体积。
  • 使用 purgecss 扫描 HTML/JS 中出现的类名,移除未引用的 CSS。
  • 将较小的 CSS 内联到 HTML 的 <style> 标签中,减少外部请求数。

4. 图片与字体优化

  • 图片压缩(imagemin 插件或在线工具)后再放入 assets
  • 使用雪碧图或 SVG sprite 合并小图标。
  • 字体文件采用按需提取(只包含用到的字符),并转为 woff2 格式。

5. 移除不必要的依赖与 polyfills

  • 定期用 depcheck 检查未使用的 npm 包。
  • 仅 polyfill 真正低版本的浏览器所需的功能,使用 @babel/preset-env 配合 useBuiltIns: 'usage' 自动按需引入 core-js,避免全量 polyfill 膨胀。

21.4.2 速度优化:让构建过程快起来

1. 选择合适的构建工具

对于新项目,Vite 在开发阶段的冷启动和热更新速度远超 Webpack,因为它基于原生 ESM,无需打包整个应用。生产构建则使用 Rollup,也具备高度优化能力。如果项目已经基于 Webpack,可以部分迁移或持续优化,不必强求切换。

2. 利用缓存

  • Webpack 5 内置了持久化缓存(cache.type: 'filesystem'),二次构建速度大幅提升。
  • Loader 层面使用 cache-loader 或在 babel-loader 中开启 cacheDirectory: true
  • Vite 的预构建(deps 预打包)结果会自动缓存到 node_modules/.vite,除非依赖变化,否则不会重复打包。

3. 并行处理

  • Webpack 中可使用 thread-loader 将耗时的 loader(如 babel-loader)分配到 worker 池并行处理。
  • 使用 esbuild-loader 替代 babel 进行 JS/TS 编译,速度快一个数量级。
  • 图片压缩、CSS 处理等任务也可通过 parallel 选项或单独线程加速。

4. 缩小构建作用域

  • Webpack 的 resolve.extensions 不要配置过多,resolve.modules 精确指定模块查找路径(避免向上递归查找 node_modules)。
  • 使用 include / exclude 精确限定 loader 处理范围,例如只对 src 目录的代码进行 Babel 转译,排除 node_modules
  • 开发时使用 devtool: 'eval-cheap-module-source-map',在速度和调试体验间取得平衡。

5. 使用更快的编译器

  • Webpack 5 配合 swc-loader(基于 Rust 的编译工具),可将打包速度提升数倍。
  • Vite 内部使用 esbuild 进行编译,本身就是最快的 JS 打包器之一。

21.4.3 分包策略:合理拆解,优化加载性能

单一入口的大包不仅下载慢,还会让浏览器解析执行时长时间阻塞。合理的分包可以拆分出基础框架代码公共业务代码异步路由代码,实现按需加载与浏览器缓存最大化利用。

1. 提取第三方库(vendors)

node_modules 下不易变更的第三方依赖(如 Vue、React、Lodash)单独打成一个 chunk,利用浏览器的长效缓存:业务代码频繁迭代时,vendor 文件哈希不变,用户无需重新下载。

Webpack 中可通过 optimization.splitChunks 配置:

// webpack.config.js
module.exports = {
  optimization: {
    splitChunks: {
      chunks: 'all',
      cacheGroups: {
        vendor: {
          test: /[\\/]node_modules[\\/]/,
          name: 'vendors',
          chunks: 'all'
        }
      }
    }
  }
};

Vite(Rollup)中通过 output.manualChunks 函数手动指定:

// vite.config.js
build: {
  rollupOptions: {
    output: {
      manualChunks: {
        vendor: ['vue', 'vue-router', 'axios'],
        lodash: ['lodash-es']
      }
    }
  }
}

2. 按路由/页面拆分(Code Splitting)

利用动态导入 import() 的语法,可以将每个路由页面的代码拆分成独立 chunk,用户导航到对应页面时才加载,加快首屏呈现。

const About = () => import('./views/About.vue');
const Home = () => import('./views/Home.vue');

Webpack 和 Vite 均原生支持动态导入,框架(Vue Router、React Router)也提供配套的异步组件定义方式。

3. 提取公共业务模块

多个页面都会用到的业务组件、工具函数,可以被提取为一个公共 chunk,避免重复打包。Webpack 的 splitChunks.minChunks 可以设置最小引用次数,Vite 需要手动通过 manualChunks 将重复出现的模块聚合。

4. 控制 chunk 大小

过大或过小的 chunk 都不理想。通常期望单个 chunk 维持在 100-500KB(未压缩)之间。过小会导致请求数过多,过大则下载时间长。可以在配置中设置 maxSize(Webpack)或通过 manualChunks 手工调节。

5. 利用异步加载的预取/预加载

对某些高概率被访问的异步 chunk,可以使用 <link rel="prefetch"><link rel="preload"> 提示浏览器提前下载,减少用户等待感。Webpack 的魔法注释 / webpackPrefetch: true / 可以自动生成 prefetch 标签。

const Profile = () => import(/* webpackPrefetch: true */ './Profile');

小结

构建优化是一个不断迭代的实践过程,而不是一成不变的配置模板。体积优化让最终用户下载更少、加载更快;速度优化让开发者的每一次保存都即时反馈;分包策略则是在带宽、缓存、代码复用之间寻找最佳平衡。善用工具提供的分析手段(webpack-bundle-analyzerrollup-plugin-visualizer),定期审视产物组成,才能有针对性地做优化,让构建产物始终保持“小而美、快而稳”。