使用WebPack CommonsChunkPlugin提取重复的JavaScript代码

10

我正在使用 WebPack CommonsChunkPlugin 来提取重复代码并减少 JavaScript 代码大小。我有两个 HTML 页面和它们的两个入口。此外,我已经添加了 ReactJs 的供应商入口。到目前为止,在 webpack.config.js 中我们有:

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']
    },
};

这是使用webpack-bundle-analyzer的配置结果:

webpack bundle analyzer output

从图中可以看出,有一些重复的代码,一些在红色区域,另一些在绿色区域。我想把这些来自主页和关于页面的js代码提取到单独的包中。为了提取红色区域的代码,即lodash库,我在webpack配置文件中添加了以下几行:

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

但是它并没有按照预期工作,lodash库的代码仍然存在于主页和关于页面的bundle中,同时webpack创建了一个名为lodash的bundle,几乎为空,不包含任何js库。

有什么解决方法吗?提取绿色区域的代码怎么样?


2个回答

3
您的问题是在每个 .js/.jsx 文件中导入第三方库而没有在公共文件(通常称为 vendor.js)中先导入它。如果您有一个导入所有依赖项的文件,并将其作为入口和 CommonsChunkPlugin 的一部分包含,那么webpack不会再次将您的库包含在最终打包文件 (home.jsabout.js) 中。这种技术在webpack文档中被称为代码拆分

vendor.js (或适合您情况的名称)

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>

请查看webpack代码分割文档:


1
我按照你说的做了,并重新运行了webpack。但结果与之前相同,lodash库仍然存在于两个文件中,只有react和react-dom被捆绑到vendor.js文件中。这是bundle-analyzer的输出:链接 - Mehran Torki
使用 require('lodash') 和 import 'lodash' 有什么区别吗?当使用 import 'lodash' 时,在供应商包中没有 lodash 代码,但当我使用 require('lodash') 时,供应商包中有 node_modules/lodash/lodash.js。你知道原因吗? - Mehran Torki
我注意到我的 webpack.config.js 和你的唯一区别是 plugins: [["lodash", { "id": ["semantic-ui-react"] }]],,尝试从 babel-loader 选项中删除它。我认为那可能是原因。如果不是,那就很奇怪了。 - The.Bear
require('lodash')import 'lodash' 有相同的效果。import 是 ES6 语法,如果可以,请尝试使用它。我认为如果你得到了不同的结果,可能是 babel 的配置出了问题。但是如果 require 对你有效,请继续使用它。 - The.Bear
你是对的,问题出在 Babel 插件选项上。我将其删除并使用了你的配置,它能够工作,但移除插件选项会带来更大的问题。当移除 babel-lodash-plugin 时,未使用的导入被打包到了 vendor 中。例如,所有的 semantic-ui-react 组件都被导入了,但大部分都没有被使用。在这个问题之前,我也遇到了打包未使用的导入的问题,并在这里提问过。解决方案是使用 babel-lodash-plugin。 - Mehran Torki
2
最终,我成功解决了问题,而不需要移除babel-lodash插件。我会发布配置文件。感谢你们的努力 :) - Mehran Torki

1

我通过将一个常用的代码块添加到插件中来解决了这个问题。因此,最终的webpack配置如下:

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']
    },
};

现在,捆绑分析器的输出如下所示:

analyzer output

如图所示,常见的semantic-ui-react和lodash库现在只在通用捆绑包中,不再重复。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接