Master Long-Term Caching and Code Splitting to Supercharge Your Webpack Builds

This article explains how to improve web application performance by leveraging persistent caching with proper cache‑control headers, versioning bundles using hash‑based filenames, extracting dependencies and runtime into separate chunks, inlining runtime scripts, applying lazy loading with dynamic imports, splitting code by routes or pages, and stabilizing module IDs with hashed IDs, all using webpack configurations for both version 3 and 4.

Yuewen Frontend Team
Yuewen Frontend Team
Yuewen Frontend Team
Master Long-Term Caching and Code Splitting to Supercharge Your Webpack Builds

Utilize Persistent Caching

After optimizing application size, the next strategy to improve load time is caching. Storing resources on the client avoids re‑downloading them on subsequent visits.

Bundle Versioning and Cache Headers

Common caching approach:

Tell the browser to cache a file for a long period (e.g., one year):

# Server header
Cache-Control: max-age=31536000

Note: If you are unfamiliar with the Cache-Control mechanism, refer to Jake Archibald’s article on caching best practices.

When a file changes, rename it to force the browser to download the new version:

<!-- before -->
<script src="./index-v15.js"></script>

<!-- after -->
<script src="./index-v16.js"></script>

This tells the browser to download the JS file once and reuse the cached copy until the filename changes or the cache expires.

With webpack you can achieve the same effect using the [chunkhash] placeholder in the filename:

// webpack.config.js
module.exports = {
  entry: './index.js',
  output: {
    filename: 'bundle.<strong>[chunkhash]</strong>.js',
    // → bundle.8e0d62a03.js
  }
};
⭐️ Note: Even if the bundle content does not change, webpack may generate a different hash (e.g., after renaming a file or compiling on a different OS). This is a known issue without a clear solution yet.

If you need to expose the generated filename to the client, you can use HtmlWebpackPlugin or WebpackManifestPlugin:

// index.html
<script src="bundle.8e0d62a03.js"></script>

Extract Dependencies and Runtime into Separate Files

Dependencies

Application dependencies change less frequently than application code. Moving them to a separate file allows the browser to cache them independently, so code changes do not force a re‑download of the libraries.

Key term: In webpack terminology, an independent file that contains application code is called a chunk .

Steps to extract dependencies:

Replace the output filename with [name].[chunkname].js:

// webpack.config.js
module.exports = {
  output: {
    // Before
    filename: 'bundle.[chunkhash].js',
    // After
    filename: '[name].[chunkhash].js'
  }
};

When webpack compiles, it uses [name] as the chunk name. Without [name] , you would have to rely on the hash, which is harder to manage.

Change entry to an object so each entry gets a name:

// webpack.config.js
module.exports = {
  // Before
  entry: './index.js',
  // After
  entry: {
    main: './index.js'
  }
};

The name "main" becomes the [name] placeholder.

In webpack 4 you can enable automatic extraction with:

// webpack.config.js (webpack 4)
module.exports = {
  optimization: {
    splitChunks: {
      chunks: 'all'
    }
  }
};

In webpack 3 you achieve the same effect with CommonsChunkPlugin:

// webpack.config.js (webpack 3)
module.exports = {
  plugins: [
    new webpack.optimize.CommonsChunkPlugin({
      name: 'vendor',
      minChunks: module => module.context && module.context.includes('node_modules')
    })
  ]
};

Webpack Runtime Code

The runtime code manages module execution. If you split code into multiple chunks, the runtime is included in the most recent chunk, causing its hash to change whenever any chunk changes.

To avoid this, move the runtime to its own file. In webpack 4 enable it with:

// webpack.config.js (webpack 4)
module.exports = {
  optimization: {
    runtimeChunk: true
  }
};

In webpack 3 you can create an empty chunk using CommonsChunkPlugin:

// webpack.config.js (webpack 3)
module.exports = {
  plugins: [
    new webpack.optimize.CommonsChunkPlugin({
      name: 'runtime',
      minChunks: Infinity
    })
  ]
};

Inline Webpack Runtime to Save an HTTP Request

Inlining the runtime reduces the number of HTTP requests (especially important for HTTP/1). With HtmlWebpackPlugin you can use InlineSourcePlugin:

// webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin');
const InlineSourcePlugin = require('html-webpack-inline-source-plugin');

module.exports = {
  plugins: [
    new HtmlWebpackPlugin({
      inlineSource: 'runtime~.+\\.js' // inline all runtime chunks
    }),
    new InlineSourcePlugin()
  ]
};

If you generate HTML manually, you can read the runtime file on the server and inject its content directly into a <script> tag.

Code Lazy Loading

Prioritize loading critical parts first and lazily load the rest using dynamic import():

// videoPlayer.js
export function renderVideoPlayer() { /* ... */ }

// comments.js
export function renderComments() { /* ... */ }

// index.js
import { renderVideoPlayer } from './videoPlayer';
renderVideoPlayer();

// …custom event listener
onShowCommentsClick(() => {
  import('./comments').then(comments => {
    comments.renderComments();
  });
});
⭐️ Note: If you compile with Babel, you need the syntax-dynamic-import plugin to avoid syntax errors.

Split Code by Routes and Pages

For single‑page applications, use import() together with router‑level code splitting (e.g., React Router or Vue Router). For traditional multi‑page apps, define multiple entry points:

// webpack.config.js
module.exports = {
  entry: {
    home: './src/Home/index.js',
    article: './src/Article/index.js',
    profile: './src/Profile/index.js'
  }
};

Webpack will generate a separate bundle for each entry, containing only the modules used by that page.

To avoid duplicate vendor code across bundles, enable optimization.splitChunks.chunks: 'all' in webpack 4 or use CommonsChunkPlugin in webpack 3.

Ensure Stable Module IDs

Webpack assigns numeric IDs to modules, which can shift when new modules are added, causing unnecessary cache invalidation. Use HashedModuleIdsPlugin to base IDs on a hash of the module path:

// webpack.config.js
module.exports = {
  plugins: [
    new webpack.HashedModuleIdsPlugin()
  ]
};

This keeps IDs stable unless the module’s path changes.

Summary

Cache bundles and use versioned filenames for long‑term caching.

Split bundles into app code, vendor libraries, and runtime.

Inline the runtime to reduce HTTP requests.

Use import() for lazy loading of non‑critical code.

Split code by route or page to avoid loading unnecessary files.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

FrontendperformancecachingWebpackCode Splitting
Yuewen Frontend Team
Written by

Yuewen Frontend Team

Click follow to learn the latest frontend insights in the cultural content industry. We welcome you to join us.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.