Chisimdiri Ejinkeonye Full-stack developer.

Improve your webpack build with the DLL plugin

3 min read 1111

The Webpack logo against a white background;

As a JavaScript developer, you’ve probably had ample opportunity to come across webpack, whether it be while bundling frontend assets with React or transpiling some TypeScript Node.js code.

Most of the time you never have to interact with webpack directly. Rather, you interact with webpack indirectly as a dependency for build tools. But if you develop these build tools, or manage your own webpack configuration, this tutorial will help you improve build times.

We’ll be using the DLL plugin, which webpack promises “to drastically improve load times” in its documentation.

How does it work?

The DLL plugin creates two things:

  • A manifest.json file
  • A bundle of modules that are not frequently changed

Without the DLL plugin enabled, webpack compiles all the files in your code base regardless of whether it’s been modified. This has the effect of making compilation times longer than necessary.

But there is a way to tell webpack not to bother recompiling libraries that hardly change: for example, libraries in your node_modules folder.

This is where the DLL plugin comes in. It bundles code you specify as rarely changing (e.g., vendor libraries), and never compiles them again, drastically improving build times.

The DLL plugin does this by creating a manifest.json file. This file is used to map import requests to the bundled module. When an import request is made to a module from other bundles, webpack checks if there is an entry in the manifest.json file to that module. If so, it skips building that module.

Overview

The DLL plugin should be used for bundles of code that hardly get changed, like your vendor bundles. As such, you’ll need a separate webpack configuration file. Learn how to create vendor bundles here.

For this tutorial, we’ll use two webpack configurations. These will be named webpack.config.js and webpack.vendor.config.js.

webpack.config.js will be your primary configuration for non-vendor code; i.e., code that is modified often.

webpack.vendor.config.js will be used for your unchanging bundles, like libraries in node_modules.

To use the DLL Plugin, two plugins must be installed in the appropriate webpack config:

DllReferencePlugin → webpack.config.js
DllPlugin → webpack.vendor.config.js



We’ll be using webpack version 4.x, as 5.x is still in beta. However, they both share similar configurations.

Configure the DLL plugin (webpack.vendor.config.js)

The DLL plugin has the following compulsory options:

  • name: This is the name of the DLL function. It can be called anything. We will call this vendor_lib.
  • path: This is the path of the outputed manifest json file. It must be an absolute path. We will store this in a folder called “build” in the root directory. The file will be called vendor-manifest.json.

To specify the path, we shall use path.join like so:

path.join(__dirname, 'build', 'vendor-manifest.json')

In the webpack.vendor.config.js file, make sure output.library is the same as the DLL plugin name option.

Include as many entry points as you want. In this example, I’ve included some really heavy-weight libraries. Your output folder doesn’t matter while using this plugin.

So here’s how webpack.vendor.config.js looks now:

var webpack = require('webpack')
const path = require('path');
module.exports = {
    mode: 'development',
    entry: {
        vendor: ['lodash', 'react', 'angular', 'bootstrap', 'd3', 'jquery', 'highcharts', 'vue']
    },
    output: {
        filename: 'vendor.bundle.js',
        path: path.join(__dirname, 'build'),
        library: 'vendor_lib'
    },
    plugins: [
        new webpack.DllPlugin({
            name: 'vendor_lib',
            path: path.join(__dirname, 'build', 'vendor-manifest.json')
        })
    ]
}

Configure the DllReferencePlugin (webpack.config.js)

The DllReferencePlugin has two compulsory fields:

  • context: This is an absolute path to the directory containing the build folder. Leave this as __dirname for this tutorial.
  • manifest: This is an absolute path to the DLL’s manifest json file. We’ll set this to path.join(__dirname, 'build', 'vendor-manifest.json').

Here’s how your webpack.config.js should look:

const webpack = require("webpack")
var path = require("path");
// const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
const SpeedMeasurePlugin = require("speed-measure-webpack-plugin");
const smp = new SpeedMeasurePlugin();
module.exports = smp.wrap({
  mode: 'development',
  entry: {
    app: ['./src/index.js']
  },
  output: {
    filename: 'main.bundle.js',
    path: path.join(__dirname, 'build')
  },
  plugins: [
    new webpack.DllReferencePlugin({
      context: __dirname,
      manifest: path.join(__dirname, 'build', 'vendor-manifest.json')
    }),
    // new BundleAnalyzerPlugin()
  ]
})

With that, we’re done setting up the DLL plugin.

Building the bundles

Generate the DLL manifest.json

You’ll first need to run webpack with the webpack.vendor.config.js configuration, which generates the vendor.manifest.json needed for webpack.config.js to work. This build could be done at the start of every development session when its configuration changes, or when the versions of the libraries in the vendor bundle change.

Add this script to your package.json file. It will create the manifest json file and the vendor bundle:

"scripts": {
    "buildVendor": "webpack --config webpack.vendor.config"
}

On subsequent code changes, you’ll only have to use webpack.config.js.

Build the main bundle

Then add a build script for the main bundle:

  "scripts": {
    "buildVendor": "webpack --config webpack.vendor.config",
    "build": "webpack --config webpack.config.js"
  }

Benchmarks

To test the plugin, I’ve instantiated a simple Vue.js app in the src/index.js file. It will import some heavy-weight dependencies:

import Vue from "vue"
import lodash from 'lodash'
import 'react'
import 'angular'
import 'bootstrap'
import 'd3'
import 'jquery'
import 'highcharts'
export default function createApp() {
  // vendor()
  const el = document.createElement("div")
  el.setAttribute("id", "app")
  document.body.appendChild(el)
  console.log("hello")
  new Vue({
    el: "#app",
    render: h => h("h1", "Hello world")
  })
}
document.addEventListener('DOMContentLoaded', () => {
  createApp()
})

To import the two bundles created by the webpack config, we need to add the following script tags to the index.html header:

<head>
  <title>Webpack DllPlugin Test</title>
  <script src="/build/vendor.bundle.js"></script>
  <script src="/build/main.bundle.js"></script>
</head>

Testing the bundles using the speed-measure-webpack-plugin gives the following benchmarks:

Specs: i5-6200U 8gb ram windows 10

With DllPlugin (Average 3 builds)
Building vendor bundle:
*3370ms

Building main bundle:
146.6ms

Without DllPlugin (Average 3 builds)
Building vendor bundle:
3312ms

Building main bundle:
3583.6ms

Assuming you only build the vendor bundle at the beginning of a coding session, and you reload say a hundred times in a session, here’s the total time you’ll spend waiting:

With DllPlugin
3370+(146.6*100) = 18030ms


More great articles from LogRocket:


Without DllPlugin
3312+(3583.6*100) = 361672ms

That’s a 95% decrease of build time! Makes for incredible productivity gains.

Conclusion

This optimization doesn’t in any way apply to your production build. It only caches the specified bundles to speed up development builds.

Check out the GitHub repo for the tutorial code.

Get setup with LogRocket's modern error tracking in minutes:

  1. Visit https://logrocket.com/signup/ to get an app ID.
  2. Install LogRocket via NPM or script tag. LogRocket.init() must be called client-side, not server-side.
  3. $ npm i --save logrocket 

    // Code:

    import LogRocket from 'logrocket';
    LogRocket.init('app/id');
    Add to your HTML:

    <script src="https://cdn.lr-ingest.com/LogRocket.min.js"></script>
    <script>window.LogRocket && window.LogRocket.init('app/id');</script>
  4. (Optional) Install plugins for deeper integrations with your stack:
    • Redux middleware
    • ngrx middleware
    • Vuex plugin
Get started now
Chisimdiri Ejinkeonye Full-stack developer.

Leave a Reply