代码分割 & SplitChunks

1. 面试题

Q: Webpack 如何配置代码分割?chunks: 'all' 的含义是什么?如何提取公共代码?

2. 核心解答

代码分割是优化 Web 应用性能最主要的手段。 核心目标是:将大 Bundle 拆解成小块,按需加载,并最大化复用

(1) 入口分割

手动配置多个 Entry,输出多个 Bundle。 缺点:如果多个 Entry 都引入了同一个库 (e.g. lodash),这个库会被重复打包进每一个 Bundle,导致整体体积膨胀。

(2) 动态导入

使用 import() 语法。Webpack 会自动将该模块打包成单独的文件,仅在运行时需要该模块时才请求。这对于路由懒加载是必须的。

(3) SplitChunksPlugin (核心)

Webpack 4+ 引入的 SplitChunksPlugin,旨在去重和分离代码。 核心配置

optimization: {
  splitChunks: {
    chunks: 'all', // 推荐:同步和异步代码都分割
    minSize: 20000, // 拆分前必须大于 20kb
    minRemainingSize: 0,
    minChunks: 1, // 至少被引用 1 次
    maxAsyncRequests: 30, // 按需加载时的最大并行请求数
    maxInitialRequests: 30, // 入口点的最大并行请求数
    enforceSizeThreshold: 50000,
    cacheGroups: { // 缓存组
      defaultVendors: { // 抽取 node_modules
        test: /[\\/]node_modules[\\/]/,
        priority: -10, // 优先级
        reuseExistingChunk: true,
      },
      default: { // 抽取公共代码
        minChunks: 2, // 至少被 2 个 Chunk 引用过
        priority: -20,
        reuseExistingChunk: true,
      },
    },
  },
},

(4) 关键参数解析

  • chunks: 'async' (默认,只分割异步代码), 'initial' (只分割同步代码), 'all' (全都分割)。
    • 面试坑点all 包含了同步代码,这意味着即使你没有用动态导入,只要你用了 reactsplitChunks 也会把它单独提出来,利用浏览器缓存。
  • cacheGroups: 定义不同的拆分规则。
    • vendors: 也就是第三方库。这些库几乎不怎么变 (Versioned),非常适合做强缓存。
    • commons: 也就是业务公共代码。如果一个 Utils 被多个页面引用,就单独提出来。

3. 面试加分项

Q: 为什么要限制 maxInitialRequests?

HTTP/1.1 浏览器对同一个域名的并发请求数有限制 (Chrome 是 6 个)。如果首屏一次性加载几十个 JS 文件,会导致后续请求阻塞 (队头阻塞)。虽然 HTTP/2 解决了这个问题,但过多的请求带来的 RTT 和 Header 开销依然不可忽视。所以 Webpack 默认限制 30 个。

4. 总结

  • Target: Reuse code, On-Demand Load within 1 request limit.
  • Config: chunks: 'all' is best practice.
  • Groups: Vendors (Third-Party, Long Cache), Commons (Business Logic).