如何配置 webpack 4 以防止块出现在任何其他包中的入口点列表中?

How to configure webpack 4 to prevent chunks from list of entry points appearing in any other bundle?

我正在做一个大项目,并试图登陆 webpack 3 -> 4 更新。这个应用程序有大约 1,000 个入口点,其中大约 10 个被认为是 "global" 或 "core" 并且保证在每个页面上。这些核心包包含供应商和非供应商代码的混合。

在 webpack 3 中,我们一直在使用 CommonsChunkPlugin 来实现这一点。这是一个简单的例子:

new webpack.optimize.CommonsChunkPlugin({
  name: 'a-global-bundle',
  minChunks: Infinity,
}),

现在使用 webpack 4 并删除了 CommonsChunkPlugin,我不清楚如何完成这种优化。

我希望能够为 webpack 提供一个入口点列表,并且出现在其中任何一个中的任何块都不会出现在任何其他包中,但我不确定如何执行此操作。我通读了 some forthcoming documentation on splitChunks,但无法拼凑出解决方案。

我已经建立了一个小回购协议作为修补的起点:https://github.com/lencioni/webpack-splitchunks-playground

我正在尝试的一个有趣的方向是为每个入口点配置 cacheGroups,并使用执行此检查的函数实现 test 选项。但是,关于这方面的文档非常少,所以我不太确定编写此测试函数的正确方法是什么,甚至不确定这是否可行。

Our goal is to configure webpack to build our assets so that any chunks that appear in any of these bundles will not appear in any other bundle.

我之前有:

            new webpack.optimize.CommonsChunkPlugin({
                name: 'vendor',
                minChunks: function (module, count) {
                    // this assumes your vendor imports exist in the node_modules directory and module should be required
                    // in at least 3 entries before it moved to common chunk
                    return (module.context && module.context.indexOf('node_modules') !== -1) && count > 2;
                }
            }),

现在的运作方式:

        optimization: {
            splitChunks: {
                cacheGroups: {
                    vendor: {
                        test: /[\/]node_modules[\/]/,
                        chunks: 'all',
                        name: 'vendor',
                        enforce: true,
                        minChunks: 3
                    }
                }
            }
        },

您当前的配置(使用 Webpack 3)使用 CommonsChunkPlugin 作为 Explicit vendor chunk:

Split your code into vendor and application.

检查 repo 的 Webpack 输出,我发现 a.bundle.js 包含以下代码:

// `react`, `react-dom` plus
console.log('core module');     // from core-module.js
console.log('core module b');   // from core-module-b.js
console.log('non-core module'); // from non-core-module.js

类似的代码在b.bundle.js里面(这个脚本的区别是最后一个console.log引用自non-core-module-b.jsconsole.log('non-core module b');)。

webpack.config.js 优化选项更新为:

optimization: {
    runtimeChunk: 'single',

    splitChunks: {
        chunks: 'all',

        cacheGroups: {
            default: {
                enforce: true,
                priority: 1
            },
            vendors: {
                test: /[\/]node_modules[\/]/,
                priority: 2,
                name: 'vendors',
                enforce: true,
                chunks: 'all'
            }
        }
    }
}

在捆绑包之间生成非重复代码。


您可以查看工作代码here。 我还为您的示例项目创建了一个 pull request

有关 code splitting and the splitChunks optimization

的更多信息

好的,所以我想我已经想出了如何做到这一点。但首先,这是使用默认 splitChunks 配置构建的样子(注意 FOO.bundle.js 是由动态导入创建的异步包):

            Asset       Size  Chunks                    Chunk Names
   core.bundle.js    605 KiB       0  [emitted]  [big]  core
  coreB.bundle.js    791 KiB       1  [emitted]  [big]  coreB
  coreC.bundle.js    791 KiB       2  [emitted]  [big]  coreC
      a.bundle.js    748 KiB       3  [emitted]  [big]  a
      b.bundle.js    792 KiB       4  [emitted]  [big]  b
      c.bundle.js    674 KiB       5  [emitted]  [big]  c
    FOO.bundle.js  709 bytes       6  [emitted]         FOO
runtime.bundle.js   7.49 KiB       7  [emitted]         runtime

如果目标是让出现在 core、coreB 和 coreC 中的任何模块都不会出现在任何其他 bundle 中,这可以通过以下配置来完成:

function coreBundleCacheGroups(coreBundles) {
  const cacheGroups = {};
  const coreChunkNames = Object.keys(coreBundles);
  const coreChunkNamesSet = new Set(coreChunkNames);


  coreChunkNames.forEach((name) => {
    cacheGroups[name] = {
      name,
      chunks: 'all',
      minSize: 0,
      minChunks: 1,
      reuseExistingChunk: true,
      priority: 10000,
      enforce: true,
      test(module, chunks) {
        if (module.depth === 0) {
          return false;
        }

        // Find first core chunk name that matches
        const partOfGlobalChunks = chunks.filter(chunk => coreChunkNamesSet.has(chunk.name));

        if (!partOfGlobalChunks.length) {
          return false;
        }

        const partOfGlobalChunksSet = new Set(partOfGlobalChunks.map(chunk => chunk.name));
        const firstCoreChunkName = coreChunkNames.find(name => partOfGlobalChunksSet.has(name));
        return firstCoreChunkName === name;
      },
    };
  });

  return cacheGroups;
}

const coreBundles = {
  core: './src/bundles/core.js',
  coreB: './src/bundles/core-b.js',
  coreC: './src/bundles/core-c.js',
};

module.exports = {
  mode: 'none',

  entry: {
    ...coreBundles,
    a: './src/bundles/a.js',
    b: './src/bundles/b.js',
    c: './src/bundles/c.js',
  },

  output: {
    filename: '[name].bundle.js',
    path: path.resolve(__dirname, 'dist')
  },

  optimization: {
    runtimeChunk: 'single',

    splitChunks: {
      cacheGroups: {
        ...coreBundleCacheGroups(coreBundles),
      },
    },
  },
};

产生以下输出:

            Asset       Size  Chunks                    Chunk Names
   core.bundle.js    605 KiB       0  [emitted]  [big]  core
  coreB.bundle.js    188 KiB       1  [emitted]         coreB
  coreC.bundle.js    1.5 KiB       2  [emitted]         coreC
      a.bundle.js   76.4 KiB       3  [emitted]         a
      b.bundle.js   2.28 KiB       4  [emitted]         b
      c.bundle.js   1.91 KiB       5  [emitted]         c
    FOO.bundle.js  622 bytes       6  [emitted]         FOO
runtime.bundle.js   7.49 KiB       7  [emitted]         runtime