Skip to content

Guides - Code splitting seems to miss a basic example #1333

Closed
@jakearchibald

Description

@jakearchibald

When reading https://webpack.js.org/guides/code-splitting-libraries/ I was trying to figure out how to create a new file based on the common parts of two entry points.

I guess this is technically what the "vendor" example is doing, but as a first-time reader I didn't realise this, because this is in a page specific to "libraries", whereas I was wanting to avoid duplication in code I'd written.

I don't know if this would help people other than me, but I would have understood the guide better if it was structured something like this:

(the following is just showing the order I think things should be introduced, not final copy)

Splitting out common code

If two of your entry points import/require the same modules, you'll end up with that code duplicated in each output file. CommonsChunkPlugin is a plugin that can extract common modules:

var webpack = require('webpack');
var path = require('path');

module.exports = function(env) {
    return {
        entry: {
            page1: './page1/index.js',
            page2: './page2/index.js'
        },
        output: {
            filename: '[name].[chunkhash].js',
            path: path.resolve(__dirname, 'dist')
        },
        plugins: [
            new webpack.optimize.CommonsChunkPlugin({
                name: 'common' // Specify the common bundle's name.
            })
        ]
    }
};

This will generate:

  • page1.ef0942b9ff95bd25c024.js - the stuff unique to page1.
  • page2.9de799314948aa4d7686.js - the stuff unique to page2.
  • common.a7b0b08b0c83bdccebbd.js - the stuff used in both page1 and page2.

Note: The filename of the chunk will inherit the filename of the output. [name].[chunkhash].js in this case.

page1 won't automatically load common, you must include both on your page, and common must execute first:

<script src="common.a7b0b08b0c83bdccebbd.js" defer></script>
<script src="page1.ef0942b9ff95bd25c024.js" defer></script>

Splitting out webpack's bootstrap

Webpack's bootstap code for dealing with importing modules is usually included with every output. However, CommonsChunkPlugin will see this duplication and move it into one file, common in this case.

However, since the bootstrap contains details about the other modules, such as their names & hashes, updating the code unique to page1 will also update common, since page1's hash will change. Adding a page3 will also update common since it will be adding details of this new module. This means the user is having to download a fresh common even though only a small part of the bootstrap has changed.

To solve this, we can extract the bootstrap into its own file:

var webpack = require('webpack');
var path = require('path');

module.exports = function(env) {
    return {
        entry: {
            page1: './page1/index.js',
            page2: './page2/index.js'
        },
        output: {
            filename: '[name].[chunkhash].js',
            path: path.resolve(__dirname, 'dist')
        },
        plugins: [
            new webpack.optimize.CommonsChunkPlugin({
                name: 'common' // Specify the common bundle's name.
            }),
            new webpack.optimize.CommonsChunkPlugin({
                name: 'manifest'
            })
        ]
    }
};

This will generate:

  • page1.ef0942b9ff95bd25c024.js - the stuff unique to page1.
  • page2.9de799314948aa4d7686.js - the stuff unique to page2.
  • common.a7b0b08b0c83bdccebbd.js - the stuff used in both page1 and page2.
  • manifest.1302ca8eccf18a208f11.js - webpack's bootstrap and module manifest.

Instances of CommonsChunkPlugin are handled in order. Our first instance extracts all the common code to common. The second instance doesn't find any common code (we've already extracted it), but it will generate a file containing the bootstrap, and remove it from the others.

Now manifest must execute first:

<script src="manifest.1302ca8eccf18a208f11.js" defer></script>
<script src="common.a7b0b08b0c83bdccebbd.js" defer></script>
<script src="page1.ef0942b9ff95bd25c024.js" defer></script>

Splitting out rarely-updated modules

We've removed all of the duplication, but you may find that a small part of one of your bundles is updating frequently. It's best to extract that, so the rest of the bundle, which perhaps updates infrequently, can cache for longer.

To do this, create an additional entry point for the parts that frequently update (or the parts that infrequently update – the important thing is they're separate):

var webpack = require('webpack');
var path = require('path');

module.exports = function(env) {
    return {
        entry: {
            page1: './page1/index.js',
            page2: './page2/index.js',
            fequentlyupdates: './page1/frequentlyupdates.js'
        },
        output: {
            filename: '[name].[chunkhash].js',
            path: path.resolve(__dirname, 'dist')
        },
        plugins: [
            new webpack.optimize.CommonsChunkPlugin({
                names: ['common', 'fequentlyupdates']
            }),
            new webpack.optimize.CommonsChunkPlugin({
                name: 'manifest'
            })
        ]
    }
};

CommonsChunkPlugin sees that fequentlyupdates is the name of an entry point, so it removes its code from the other modules.

Note: I expected two instances of CommonsChunkPlugin to work. One for common, then one for fequentlyupdates, but it throws an error. I don't know if this is a bug with webpack or a bug in my mental model.

Splitting out a library

Library modules are typically rarely updated as part of your build. So let's say instead of fequentlyupdates, you wanted to extract moment, the date formatting library:

var webpack = require('webpack');
var path = require('path');

module.exports = function(env) {
    return {
        entry: {
            page1: './page1/index.js',
            page2: './page2/index.js',
            moment: 'moment'
        },
        output: {
            filename: '[name].[chunkhash].js',
            path: path.resolve(__dirname, 'dist')
        },
        plugins: [
            new webpack.optimize.CommonsChunkPlugin({
                names: ['common', 'moment']
            }),
            new webpack.optimize.CommonsChunkPlugin({
                name: 'manifest'
            })
        ]
    }
};

Here we've replaced the path to the fequentlyupdates script with the module name.

Splitting out everything in node_modules

Assuming you update dependencies less-frequently than your application code, you may want to split everything within node_modules:

var webpack = require('webpack');
var path = require('path');

module.exports = function(env) {
    return {
        entry: {
            page1: './page1/index.js',
            page2: './page2/index.js'
        },
        output: {
            filename: '[name].[chunkhash].js',
            path: path.resolve(__dirname, 'dist')
        },
        plugins: [
            new webpack.optimize.CommonsChunkPlugin({
                name: 'common'
            }),
            new webpack.optimize.CommonsChunkPlugin({
                name: 'vendor',
                minChunks: function (module) {
                    return module.context && module.context.indexOf('node_modules') !== -1;
                }
            }),
            new webpack.optimize.CommonsChunkPlugin({
                name: 'manifest'
            })
        ]
    }
};

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions