Code splitting and SplitChunksPlugin

webpack · Advanced · 15 min

Shipping less JavaScript up front is the biggest lever for load performance.

webpack creates separate chunks in three ways:

  • Entry points: each entry is its own chunk (duplicating shared code unless you configure sharing).
  • Dynamic imports: import("./chart.js") returns a promise and puts the module and its dependencies in a chunk loaded on demand. Route-level splitting with React.lazy uses this.
  • SplitChunksPlugin (optimization.splitChunks): extracts shared modules and vendor code into common chunks.

Magic comments tune dynamic imports: import(/* webpackChunkName: "chart" */ "./chart.js") names the chunk, webpackPrefetch: true fetches it in idle time, webpackPreload: true loads it in parallel with the parent.

SplitChunksPlugin defaults apply to async chunks only. Set chunks: "all" to also split initial chunks, so vendor code shared by entries is loaded once. Cache groups define how modules are grouped:

  • defaultVendors collects node_modules modules; default collects modules used by at least two chunks.
  • Add your own, e.g. a react group for React packages with a higher priority.
  • minSize (20 KB by default; smaller groups are not split out), maxSize, minChunks, maxAsyncRequests and maxInitialRequests limit how aggressive splitting is.

Inspect the result with webpack-bundle-analyzer. Too many tiny chunks cause request overhead; one giant vendor chunk invalidates the cache on every dependency update.

Example

// src/index.js
document.querySelector("#show-chart").addEventListener("click", async () => {
  const { drawChart } = await import(
    /* webpackChunkName: "chart", webpackPrefetch: true */ "./chart.js"
  );
  drawChart();
});

// webpack.config.js (excerpt)
module.exports = {
  optimization: {
    splitChunks: {
      chunks: "all",
      cacheGroups: {
        react: {
          test: /[\\/]node_modules[\\/](react|react-dom|scheduler)[\\/]/,
          name: "react",
          priority: 20,
        },
      },
    },
  },
};
// Output: main.[hash].js, react.[hash].js, chart.[hash].js (loaded on click)

Remember

  • Dynamic import() creates on-demand chunks
  • Magic comments: webpackChunkName, webpackPrefetch, webpackPreload
  • splitChunks.chunks: 'all' shares vendor code across entries
  • Cache groups with priorities control grouping

Watch out for

  • Prefetching everything, which wastes bandwidth on chunks users rarely need.

Check yourself

  1. By default, which chunks does SplitChunksPlugin optimize?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: webpack.js.org/guides/code-splitting/