【问题标题】:Webpack 4 migration CommonsChunkPluginWebpack 4 迁移 CommonsChunkPlugin
【发布时间】:2018-08-07 15:09:46
【问题描述】:

我需要帮助将以下代码从 webpack 3 迁移到 4。

new webpack.optimize.CommonsChunkPlugin({
    minChunks: module => module.context && module.context.indexOf("node_modules") !== -1,
    name: "vendor",
    chunks: ["main"]
})

我有两个入口文件,并且只希望第一个的依赖项包含在供应商块中。第二个条目的依赖项应该都保留在自己的包中。

【问题讨论】:

    标签: javascript webpack webpack-3 commonschunkplugin webpack-4


    【解决方案1】:

    请注意,我通过在 webpack.common.js 中更改此问题更正了该问题:

      plugins: [
        new webpack.optimize.CommonsChunkPlugin({
          name: ['vendor']
        })
        ]
    

    到这里:

      optimization: {
          runtimeChunk: "single", // enable "runtime" chunk
          splitChunks: {
              cacheGroups: {
                  vendor: {
                      test: /[\\/]node_modules[\\/]/,
                      name: "vendor",
                      chunks: "all"
                  }
              }
          }
      },
    

    【讨论】:

      【解决方案2】:

      As of webpack v4 the CommonsChunkPlugin is deprecated.

      我们已弃用并删除 CommonsChunkPlugin,并已替换 它具有一组默认值和易于覆盖的 API,称为 optimization.splitChunks.

      webpack.optimize.CommonsChunkPlugin has been removed, 
      please use config.optimization.splitChunks instead.
      

      已弃用

      您不再需要使用这些插件:

      DedupePlugin has been removed too in v4

      NoEmitOnErrorsPlugin -> optimization.noEmitOnErrors(在生产模式下默认开启) ModuleConcatenationPlugin -> optimization.concatenateModules(在 prod 模式下默认开启) NamedModulesPlugin -> optimization.namedModules(在开发模式下默认开启)


      针对 webpack 4 的建议

      使用mini-css-extract-plugin 而不是text-extract-plugin。 使用webpack-bundle-analyzer 以图形方式分析您的捆绑输出。

      入口脚本是您应用程序的真正“入口脚本”,不要在webpack.config.js 中将供应商文件显式添加到entry:。 SPA 应用程序有一个入口,而经典的ASP.NET MVC 应用程序等多页应用程序有多个入口点。 Webpack 将从你的入口脚本构建一个依赖图,并为你的应用生成优化的包。

      如果你想从旧的 webpack 版本迁移,最好查看migration guide

      Tree Shaking(死代码消除)仅在生产模式下启用。


      Webpack 4,捆绑资产的新方式

      你必须从你的脑海中移除你的 CommonsChunkPlugin 思维

      !!! Meanwhile the webpack doc has been updated, a section SplitChunks was added !!!

      It follows a new philosophy:

      Webpack 4 现在默认自动进行优化。它会根据以下条件分析您的依赖关系图并创建最佳捆绑包(输出):

      1. 可以共享新块或模块来自 node_modules 文件夹
      2. 新块将大于 30kb(在 min+gz 之前)
      3. 按需加载块时的最大并行请求数
      4. 初始页面加载时的最大并行请求数

      所有这些都可以使用 SplitChunksPlugin 进行调整! (see SplitChunksPlugin documentation)

      A more detailed explanation 了解如何使用新的optimization.splitChunks API。



      CommonsChunkPlugin 被移除,因为它有很多问题:

      • 这会导致下载的代码比需要的多。
      • 在异步块上效率低下。
      • 很难使用。
      • 实现难以理解。

      SplitChunksPlugin 也有一些很棒的属性:

      • 它从不下载不需要的模块(只要您不通过名称强制块合并)
      • 它在异步块上也很有效
      • 异步块默认开启
      • 它使用多个供应商块处理供应商拆分
      • 使用更简单
      • 它不依赖于块图 hacks
      • 大部分是自动的

      --> Source


      关于您的问题,您希望将 entry1 和 entry2 的所有部门拆分为单独的包。

            optimization: {
              splitChunks: {
                cacheGroups: {   
                  "entry1-bundle": {
                    test: /.../,   // <-- use the test property to specify which deps go here
                    chunks: "all",
                    name: "entry1-bundle",
       /** Ignore minimum size, minimum chunks and maximum requests and always create chunks for this cache group */
                    enforce: true,
                    priority: ..  // use the priority, to tell where a shared dep should go
                  },
                  "entry2-bundle": {
                    test: /..../, // <-- use the test property to specify which deps go here
                    chunks: "all",
                    name: "entry2-bundle",
                    enforce: true,
                    priority: ..
                  }
                }
              }
            },
      

      如果不添加 optimization:splitChunks 条目,default setting 如下

      splitChunks: {
        chunks: 'async',
        minSize: 30000,
        minRemainingSize: 0,
        maxSize: 0,
        minChunks: 1,
        maxAsyncRequests: 6,
        maxInitialRequests: 4,
        automaticNameDelimiter: '~',
        automaticNameMaxLength: 30,
        cacheGroups: {
          vendors: {
            test: /[\\/]node_modules[\\/]/,
            priority: -10
          },
          default: {
            minChunks: 2,
            priority: -20,
            reuseExistingChunk: true
          }
        }
      }
      

      您可以将 optimization.splitChunks.cacheGroups.default 设置为 false 以禁用 default 缓存组,vendors 缓存组也是如此!

      这里有一些其他SplitChunks configuration examples 的解释。


      SplitChunksOptionsCachGroupOptionsOptimization 的最新接口实现可以在 here 找到。

      以下接口定义可能不是 100% 准确,但适合简单概述:

      SplitChunksOptions接口

      interface SplitChunksOptions {
          /** Select chunks for determining shared modules (defaults to \"async\", \"initial\" and \"all\" requires adding these chunks to the HTML) */
          chunks?: "initial" | "async" | "all" | ((chunk: compilation.Chunk) => boolean);
          /** Minimal size for the created chunk */
          minSize?: number;
          /** Minimum number of times a module has to be duplicated until it's considered for splitting */
          minChunks?: number;
          /** Maximum number of requests which are accepted for on-demand loading */
          maxAsyncRequests?: number;
          /** Maximum number of initial chunks which are accepted for an entry point */
          maxInitialRequests?: number;
          /** Give chunks created a name (chunks with equal name are merged) */
          name?: boolean | string | ((...args: any[]) => any);
          /** Assign modules to a cache group (modules from different cache groups are tried to keep in separate chunks) */
          cacheGroups?: false | string | ((...args: any[]) => any) | RegExp | { [key: string]: CacheGroupsOptions };
      }
      

      CacheGroupsOptions接口:

      interface CacheGroupsOptions {
          /** Assign modules to a cache group */
          test?: ((...args: any[]) => boolean) | string | RegExp;
          /** Select chunks for determining cache group content (defaults to \"initial\", \"initial\" and \"all\" requires adding these chunks to the HTML) */
          chunks?: "initial" | "async" | "all" | ((chunk: compilation.Chunk) => boolean);
          /** Ignore minimum size, minimum chunks and maximum requests and always create chunks for this cache group */
          enforce?: boolean;
          /** Priority of this cache group */
          priority?: number;
          /** Minimal size for the created chunk */
          minSize?: number;
          /** Minimum number of times a module has to be duplicated until it's considered for splitting */
          minChunks?: number;
          /** Maximum number of requests which are accepted for on-demand loading */
          maxAsyncRequests?: number;
          /** Maximum number of initial chunks which are accepted for an entry point */
          maxInitialRequests?: number;
          /** Try to reuse existing chunk (with name) when it has matching modules */
          reuseExistingChunk?: boolean;
          /** Give chunks created a name (chunks with equal name are merged) */
          name?: boolean | string | ((...args: any[]) => any);
      }
      

      Optimization接口

      interface Optimization {
          /**
           *  Modules are removed from chunks when they are already available in all parent chunk groups.
           *  This reduces asset size. Smaller assets also result in faster builds since less code generation has to be performed.
           */
          removeAvailableModules?: boolean;
          /** Empty chunks are removed. This reduces load in filesystem and results in faster builds. */
          removeEmptyChunks?: boolean;
          /** Equal chunks are merged. This results in less code generation and faster builds. */
          mergeDuplicateChunks?: boolean;
          /** Chunks which are subsets of other chunks are determined and flagged in a way that subsets don’t have to be loaded when the bigger chunk has been loaded. */
          flagIncludedChunks?: boolean;
          /** Give more often used ids smaller (shorter) values. */
          occurrenceOrder?: boolean;
          /** Determine exports for each module when possible. This information is used by other optimizations or code generation. I. e. to generate more efficient code for export * from. */
          providedExports?: boolean;
          /**
           *  Determine used exports for each module. This depends on optimization.providedExports. This information is used by other optimizations or code generation.
           *  I. e. exports are not generated for unused exports, export names are mangled to single char identifiers when all usages are compatible.
           *  DCE in minimizers will benefit from this and can remove unused exports.
           */
          usedExports?: boolean;
          /**
           *  Recognise the sideEffects flag in package.json or rules to eliminate modules. This depends on optimization.providedExports and optimization.usedExports.
           *  These dependencies have a cost, but eliminating modules has positive impact on performance because of less code generation. It depends on your codebase.
           *  Try it for possible performance wins.
           */
          sideEffects?: boolean;
          /** Tries to find segments of the module graph which can be safely concatenated into a single module. Depends on optimization.providedExports and optimization.usedExports. */
          concatenateModules?: boolean;
          /** Finds modules which are shared between chunk and splits them into separate chunks to reduce duplication or separate vendor modules from application modules. */
          splitChunks?: SplitChunksOptions | false;
          /** Create a separate chunk for the webpack runtime code and chunk hash maps. This chunk should be inlined into the HTML */
          runtimeChunk?: boolean | "single" | "multiple" | RuntimeChunkOptions;
          /** Avoid emitting assets when errors occur. */
          noEmitOnErrors?: boolean;
          /** Instead of numeric ids, give modules readable names for better debugging. */
          namedModules?: boolean;
          /** Instead of numeric ids, give chunks readable names for better debugging. */
          namedChunks?: boolean;
          /** Defines the process.env.NODE_ENV constant to a compile-time-constant value. This allows to remove development only code from code. */
          nodeEnv?: string | false;
          /** Use the minimizer (optimization.minimizer, by default uglify-js) to minimize output assets. */
          minimize?: boolean;
          /** Minimizer(s) to use for minimizing the output */
          minimizer?: Array<Plugin | Tapable.Plugin>;
          /** Generate records with relative paths to be able to move the context folder". */
          portableRecords?: boolean;
      }
      }
      

      【讨论】:

      • 所以基本上因为 webpack 4 就像魔术一样工作,我不需要做任何事情来拆分我的块?
      • 如果你有一个相当小的应用程序是没有意义的,因为它应该吐出最佳的包。我建议你开始一个新的 webpack 4 项目,但是从很小的地方开始。添加依赖并编译包。
      【解决方案3】:

      我花了一些时间才弄明白,但对我来说关键的实现是 webpack 4 中的 chunks 参数现在接受了一个函数,它只允许你包含一个特定的条目。我假设这是最近的更改,因为在发布时它不在官方文档中。

      splitChunks: {
        cacheGroups: {
          vendor: {
            name: 'vendor',
            chunks: chunk => chunk.name == 'main',
            reuseExistingChunk: true,
            priority: 1,
            test: module =>
              /[\\/]node_modules[\\/]/.test(module.context),
            minChunks: 1,
            minSize: 0,
          },
        },
      },
      

      【讨论】:

      • 这在 webpack 5 中仍然有效。
      【解决方案4】:

      我有两个入口文件,并且只希望第一个的依赖项包含在供应商块中。第二个条目的依赖项应该都保留在自己的包中。

      假设您的入口点是 mainsecondary

      entry: {
          main: 'path-to/main.js',
          secondary: 'path-to/secondary.js'
      }
      

      使用 您只能从main 块中提取vendors 模块,但使用您要创建的cacheGroupstest 函数将secondary 中引用的其他第三方模块留在该块中.

      optimization: {
          splitChunks: {
              cacheGroups: {
                  vendors: {
                      name: 'vendors',
                      chunks: 'all',
                      reuseExistingChunk: true,
                      priority: 1,
                      enforce: true,
                      test(module, chunks) {
                          const name = module.nameForCondition && module.nameForCondition();
                          return chunks.some(chunk => {
                              return chunk.name === 'main' && /[\\/]node_modules[\\/]/.test(name);
                          });
                      }
                  },
                  secondary: {
                      name: 'secondary',
                      chunks: 'all',
                      priority: 2,
                      enforce: true,
                      test(module, chunks) {
                          return chunks.some(chunk => chunk.name === 'secondary');
                      }
                  }
              }
          }
      }
      

      【讨论】:

      • 我做了一个类似的解决方案,这导致代码永远不会运行 - 没有添加运行时。
      • 要为 webpack 运行时添加单独的脚本,您需要在 optimization 对象中设置运行时属性:runtimeChunk: 'single'。你能分享你的配置吗?并提供更多细节?另请查看Webpack-4 Demo 以获得指导。希望对您有所帮助!
      • 我有一个和你的答案一样的配置 - 多个入口点和一个与“次要”入口点同名的 chunkDef。无论我在运行时块中配置了什么,除非我运行供应商块,否则我永远无法运行辅助块,这不应该是辅助块的依赖项。我已经花了几天时间,我正在放弃。就我而言,多配置会是更好的解决方案。
      • @kvetis 我通过将块更改为 'async' 而不是 'all' 来解决这个问题,并且我通过使用 webpack 的 'import(xx)' 语句使 'main' 条目变为 'async'假如。只需不要触摸“辅助”条目,这样它就不会被视为“异步”块。
      猜你喜欢
      • 1970-01-01
      • 2018-08-28
      • 2018-09-01
      • 2019-02-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-09-11
      • 1970-01-01
      相关资源
      最近更新 更多