Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Extract duplicate javascript code using WebPack CommonsChunkPlugin

I'm using WebPack CommonsChunkPlugin to extract duplicate code and reduce JavaScript code size. I have two html pages and two entries for them. Also i've added ReactJs vendor entry. So far, in webpack.config.js we have:

var path = require("path");
var webpack = require('webpack');
var BundleTracker = require('webpack-bundle-tracker');
var BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;

module.exports = {
    context: __dirname,
    entry: {
        react: ["react", "react-dom"],
        home: './assets/js/home.jsx',
        about: './assets/js/about.jsx',
    },

    output: {
        path: path.resolve('./assets/bundles/'),
        filename: "[name].js",
    },

    plugins: [
        new BundleTracker({filename: './webpack-stats.json'}),

        new webpack.optimize.CommonsChunkPlugin({
            name: 'react',
            minChunks: Infinity
        }),

        new BundleAnalyzerPlugin(),
    ],

    module: {
        rules: [
            {
              test: /\.jsx?$/, 
              exclude: /node_modules/,
              loader: 'babel-loader',
              options: { 
                  plugins: [["lodash", { "id": ["semantic-ui-react"] }]],
                  presets: ["es2015", "react"]
              }  
            },
        ],
    },

    resolve: {
        modules: ['node_modules', 'bower_components'],
        extensions: ['*', '.js', '.jsx']
    },
};

This configuration result with webpack-bundle-analyzer:

webpack bundle analyzer output

As you can see, there are some duplicate code, some in red area and some other in green area. I want to extract this js codes from home and about bundles into a separate bundle. To extract red area code, namely lodash library, i added these lines to webpack config:

new webpack.optimize.CommonsChunkPlugin({
    name: 'lodash',
    minChunks: function(module, count) {
        return module.context.indexOf('node_modules/lodash') >= 0;
    }
}), 

But it's not working as expected and lodash library code is still in both home and about bundles, also webpack creates a bundle named lodash that is almost empty and contains no js library.

Any idea on how to fix it ? How about extracting green are codes?

like image 623
Mehran Torki Avatar asked Aug 01 '17 21:08

Mehran Torki


2 Answers

Your problem is that your are importing third party libs in each .js/.jsx file without importing it previously in a common file (normally called vendor.js).
If you have this file that import all you dependencies and you include it as entry and to CommonsChunkPlugin, webpack won't include again your libs in your final bundles (home.js and about.js). The technique is called code splitting in webpack docs.

vendor.js (or a name that fit for your case)

import 'react';
import 'react-dom';
import 'lodash';
import 'semantic-ui-react';
//... all your npm packages

webpack.config.js

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

module.exports = {
    context: __dirname,
    entry: {
        vendor: './assets/js/vendor.js,
        home: './assets/js/home.jsx',
        about: './assets/js/about.jsx',
    },
    output: {
        path: path.resolve('./assets/bundles/'),
        filename: '[name].js',
    },
    plugins: [
        new webpack.optimize.CommonsChunkPlugin({
            name: 'vendor',
            minChunks: Infinity
        }),
    ],

    //Rest of Your config ...
};

index.html

<body>
    <!-- AFTER YOUR HTML CODE -->

    <script type="text/javascript" src="/assets/bundles/vendor.js"></script>
    <script type="text/javascript" src="/assets/bundles/home.js"></script>
    <script type="text/javascript" src="/assets/bundles/about.js"></script>
</body>


Check webpack code splitting docs:

  • Old docs: https://webpack.github.io/docs/code-splitting.html#split-app-and-vendor-code
  • New Docs: https://webpack.js.org/plugins/commons-chunk-plugin/#explicit-vendor-chunk
like image 137
The.Bear Avatar answered Nov 08 '22 17:11

The.Bear


I managed to solve the problem by adding a common chunk to plugins. So final webpack config is :

var path = require("path");
var webpack = require('webpack');
var BundleTracker = require('webpack-bundle-tracker');

module.exports = {

    context: __dirname,

    entry: {
        react: ["react", "react-dom"],
        home: './assets/js/home.jsx',
        about: './assets/js/about.jsx',
    },

    output: {
        path: path.resolve('./assets/bundles/'),
        filename: "[name].js",
    },

    plugins: [

        new BundleTracker({filename: './webpack-stats.json'}),

        new webpack.optimize.CommonsChunkPlugin({
            name: 'react',
            filename: '[name].js',
            minChunks: Infinity,
        }),

        new webpack.optimize.CommonsChunkPlugin({
            name: 'common',
            chunks: ['home', 'about'],
            filename: '[name].js',
        }),
    ],

    module: {
        rules: [
            {
                test: /\.jsx?$/, 
                exclude: /node_modules/,
                loader: 'babel-loader',
                options: { 
                    plugins: [
                      ["lodash", { "id": ["semantic-ui-react"] }]
                    ],
                    presets: ["es2015", "react"]
                }
            },
        ],
    },

    resolve: {
        modules: ['node_modules', 'bower_components'],
        extensions: ['*', '.js', '.jsx']
    },
};

And now bundle analyzer output is like this:

analyzer output

As it's shown in the picture, common semantic-ui-react and lodash libraries are now just in common bundle and not duplicated anymore.

like image 21
Mehran Torki Avatar answered Nov 08 '22 16:11

Mehran Torki