Code Splitting with Webpack

Table of Contents

  1. Introduction
  2. Setup Webpack Project with Django
  3. Load Webpack bundles in Django
  4. Linting in Webpack
  5. Load Webpack hash bundle in Django
  6. Code splitting with Webpack
  7. How to config HMR with Webpack and Django
  8. How to use HtmlWebpackPlugin to load Webpack bundle in Django

If you want a quick start with Webpack and Django, please check python-webpack-boilerplate

Objective

By the end of this chapter, you should be able to:

  1. Understand the code splitting workflow in Webpack.
  2. Config splitChunks to control the splitting behavior.
  3. Load bundle file with splitChunks and Django static templatetag.

Background

What should we do When the bundle file grow? (new features, new 3-party library)

It's also good practice to extract third-party libraries, such as lodash or react, to a separate vendor chunk as they are less likely to change than our local source code. This step will allow clients to request even less from the server to stay up to date.

This can be done by using the splitChunks option of the SplitChunksPlugin

splitChunks

Let's update webpack/webpack.common.js

optimization: {
  splitChunks: {
    chunks: 'all',
    cacheGroups: {
      vendor: {
        test: /[\\/]node_modules[\\/]/,
        name(module) {
          // get the name. E.g. node_modules/packageName/not/this/part.js
          // or node_modules/packageName
          const packageName = module.context.match(
            /[\\/]node_modules[\\/](.*?)([\\/]|$)/
          )[1];

          // npm package names are URL-safe, but some servers don't like @ symbols
          return `npm.${packageName.replace("@", "")}`;
        }
      },
    }
  },
},

Notes:

  1. We created a vendor group in the optimization.splitChunks.cacheGroups
  2. The test means it would work on all packages which are in node_modules
  3. The name function would be used to generate name for the chunk.
(frontend)$ npm run build
(frontend)$ cat webpack-stats.json
{
  "status": "done",
    "chunks": {
    "app": [
      "js/npm.jquery.e2d46062.js",
      "js/npm.bootstrap.a31ec5e3.js",
      "js/app.fd53aa41.js",
      "css/app-e3e173a2b456b7aaf3a4.css"
    ]
  },
}
  1. As we can see, npm.jquery and npm.bootstrap chunks are generated.
  2. Actually, if we import new packages in the src/index.js (such as Axios), new chunks would be also generated.

The above config is to help us better understand the cacheGroups

Manual config

In some cases, we can use cacheGroups to control the code splitting behavior.

Let's update webpack/webpack.common.js

optimization: {
  splitChunks: {
    chunks: 'all',
    cacheGroups: {
      vendor: {
        test: /[\\/]node_modules[\\/]/,
        name: 'vendors',
      },
      jquery: {
        test: /[\\/]node_modules[\\/](jquery)[\\/]/,
        name: 'jquery',
      },
    }
  },
},

Notes:

  1. We created a vendor group which has name vendor
  2. We created a jquery group, which has name jquery, the test would make sure the chunk only match jquery.
  3. If we want the test match multiple packages, you can use Regex like this /[\\/]node_modules[\\/](react|react-dom)[\\/]/
{
  "status": "done",
  "chunks": {
    "app": [
      "js/jquery.5007ce70.js",
      "js/vendors.1acdbaad.js",
      "js/app.44ab1a1c.js",
      "css/app-e3e173a2b456b7aaf3a4.css"
    ]
  },
  "publicPath": "/static/",
  "assets": {
    "js/app.44ab1a1c.js": {
      "name": "js/app.44ab1a1c.js",
      "publicPath": "/static/js/app.44ab1a1c.js"
    },
    "js/jquery.5007ce70.js": {
      "name": "js/jquery.5007ce70.js",
      "publicPath": "/static/js/jquery.5007ce70.js"
    },
    "js/vendors.1acdbaad.js": {
      "name": "js/vendors.1acdbaad.js",
      "publicPath": "/static/js/vendors.1acdbaad.js"
    },
    "public/.gitkeep": {
      "name": "public/.gitkeep",
      "publicPath": "/static/public/.gitkeep"
    },
    "js/jquery.5007ce70.js.LICENSE.txt": {
      "name": "js/jquery.5007ce70.js.LICENSE.txt",
      "publicPath": "/static/js/jquery.5007ce70.js.LICENSE.txt"
    },
    "js/vendors.1acdbaad.js.LICENSE.txt": {
      "name": "js/vendors.1acdbaad.js.LICENSE.txt",
      "publicPath": "/static/js/vendors.1acdbaad.js.LICENSE.txt"
    },
    "js/app.44ab1a1c.js.map": {
      "name": "js/app.44ab1a1c.js.map",
      "publicPath": "/static/js/app.44ab1a1c.js.map"
    },
    "js/jquery.5007ce70.js.map": {
      "name": "js/jquery.5007ce70.js.map",
      "publicPath": "/static/js/jquery.5007ce70.js.map"
    },
    "js/vendors.1acdbaad.js.map": {
      "name": "js/vendors.1acdbaad.js.map",
      "publicPath": "/static/js/vendors.1acdbaad.js.map"
    },
    "css/app-e3e173a2b456b7aaf3a4.css": {
      "name": "css/app-e3e173a2b456b7aaf3a4.css",
      "publicPath": "/static/css/app-e3e173a2b456b7aaf3a4.css"
    },
    "css/app-e3e173a2b456b7aaf3a4.css.map": {
      "name": "css/app-e3e173a2b456b7aaf3a4.css.map",
      "publicPath": "/static/css/app-e3e173a2b456b7aaf3a4.css.map"
    },
  }
}

Notes:

  1. The jquery chunk contains code from jquery
  2. The vendors chunk contains code from the bootstrap

It is also recommended to use webpack-bundle-analyzer if you want to customize splitChunks.

Django

In the previous chapters, when our JS code size is small, we can use Django static templatetag to import js code like this

<script src="{% static 'js/vendors.js' %}"></script>
<script src="{% static 'js/app.js' %}"></script>
  1. When the JS code size grow bigger, we can config code splitting to generate some specific chunk file.
  2. And then, we can config Webpack to removehash from the generated bundle file.
  3. In the end, we can import JS in Django template like this
<script src="{% static 'js/vendors-aaa.js' %}"></script>
<script src="{% static 'js/vendors-bbb.js' %}"></script>
<script src="{% static 'js/vendors-ccc.js' %}"></script>
<script src="{% static 'js/app.js' %}"></script>

Conclusion

  1. Introduction
  2. Setup Webpack Project with Django
  3. Load Webpack bundles in Django
  4. Linting in Webpack
  5. Load Webpack hash bundle in Django
  6. Code splitting with Webpack
  7. How to config HMR with Webpack and Django
  8. How to use HtmlWebpackPlugin to load Webpack bundle in Django

If you want a quick start with Webpack and Django, please check python-webpack-boilerplate

Launch Products Faster with Django

Unlock the power of Django combined with Hotwire through SaaS Hammer. Supercharge productivity, tap into Python's rich ecosystem, and focus on perfecting your product!

Michael Yin

Michael Yin

Michael is a Full Stack Developer who loves writing code, tutorials about Django, and modern frontend techs.

He has published some tech course on testdriven.io and ebooks on leanpub.

© 2024 SaaS Hammer