Webpack CommonsChunkPlugin 理解

CommonsChunkPlugin配置简介

最近读了一下webpack的文档,读到CommonsChunkPlugin这个插件,深深折服于webpack的强大,同时也产生了一些本身的疑问。javascript

首先,CommonsChunkPlugin这个插件是用来提取公共代码的,经过将公共模块提取出来,只在页面加载的时候引入一次,提高应用的加载效率。java

顺便提一下,chunk其实就是代码块的意思,多是一个或多个模块,通常提取后就是一个js文件。node

CommonsChunkPlugin有中文翻译的文档,可是感受并非很通顺,英文文档看完也有一些疑惑,好比minChunks究竟是作什么用的,怎么用?chunks是什么?webpack

配置项

首先贴一下文档。web

{
  name: "string",
  names: "string[]",
  filename: "string",
  minChunks: "number|Infinity|function(module, count) -> boolean",
  chunks: "string[]",
  children: "boolean",
  async: "boolean|string",
  minSize: "number"
}
  • name和names:chunk的名称,若是这个chunk已经在entry中定义,该chunk会被直接提取;若是没有定义,则生成一个空的chunk来提取其余全部chunk的公共代码。
  • filename:能够指定提取出的公共代码的文件名称,可使用output配置项中文件名的占位符。未定义时使用name做为文件名。
  • chunks:能够指定要提取公共模块的源chunks,指定的chunk必须是公共chunk的子模块,若是没有指定则使用全部entry中定义的入口chunk。
  • minChunks:在一个模块被提取到公共chunk以前,它必须被最少minChunks个chunk所包含(通俗的说就是一个模块至少要被minChunks个模块所引用,才能被提取到公共模块。)。minChunks参数有三点须要说明:缓存

    1. 该数字必须不小于2或者不大于chunks的个数。默认值等于chunks的个数。
    2. 若是指定了Infinity,则建立一个公共chunk,可是不包含任何模块,内部是一些webpack生成的runtime代码和chunk自身包含的模块(若是chunk存在的话)。
    3. 用户也能够定制本身的逻辑去生成代码。
  • minSize:表示公共chunk建立所须要的全部模块的最小体积。
  • async:若是设置为true,一个异步的公共chunk将会做为options.name的子块和options.chunks的兄弟块被建立。该chunk会与options.chunks并行加载。你也可使用该参数指定输出的文件名。
  • children:若是设置为true,公共chunk的全部子模块将会被选中。

例子

咱们看一个简单的例子。app

module.exports = {
    entry: {
        app: './src/index.js',
        vender: [
            'lodash',
            'otherlib'
        ]
    },
    plugins: [
        new webpack.optimize.CommonsChunkPlugin({
            name: 'vender'
        })
    ],
    output: {
        // 使用Hash来命名文件,实现文件缓存的功能。当文件内容发生变化,文件名会随之改变。
        filename: '[name].[chunkhash].js', 
        path: path.resolve(__dirname, 'dist')
    }
};

上面的代码中定义了两个入口,app和vender(公共库),plugins中使用CommonsChunkPlugin提取vender。异步

vender是咱们提取出来的公共chunk,一般不会被修改,因此理应在每次编译后文件名保持一致。然而,咱们尝试修改入口文件index.js会发现,vender的文件名会发生变化。async

缘由呢上面提到过,因为每次编译会致使vender的module.id发生变化,内部的runtime代码随之发生改变。ide

解决方案有如下几种:

  1. 使用NamedModulesPlugin插件,用文件路径而非默认的数字ID来做为模块标识。
  2. 使用HashedModuleIdsPlugin插件,用相对路径的Hash值来做为模块标识。推荐在生产环境中使用。
  3. 将runtime部分的代码提取到一个单独的文件中,代码以下。
module.exports = {
    entry: {
        app: './src/index.js',
        vender: [
            'lodash'
        ]
    },
    plugins: [
        new webpack.optimize.CommonsChunkPlugin({
            name: 'vender',
            minChunks: Infinity
        }),
        new webpack.optimize.CommonsChunkPlugin({
            name: 'manifest',
            chunks: ['vender']
        })
    ],
    output: {
        filename: '[name].[chunkhash].js',
        path: path.resolve(__dirname, 'dist')
    }
};

代码中再次使用了CommonsChunkPlugin,从vender中提取出了名为manifest的运行时代码。

分析Vue的使用方式

Vue在production模式中使用以下配置

// split vendor js into its own file
new webpack.optimize.CommonsChunkPlugin({
  name: 'vendor',
  minChunks: function (module, count) {
    // any required modules inside node_modules are extracted to vendor
    return (
      module.resource &&
      /\.js$/.test(module.resource) &&
      module.resource.indexOf(
        path.join(__dirname, '../node_modules')
      ) === 0
    )
  }
}),
// extract webpack runtime and module manifest to its own file in order to
// prevent vendor hash from being updated whenever app bundle is updated
new webpack.optimize.CommonsChunkPlugin({
  name: 'manifest',
  chunks: ['vendor']
})

自定义minChunks

minChunks配置项有两个参数,module为当前要提取的源chunk中所包含的模块,count表示模块的个数。
自定义函数会被循环调用,直到遍历完全部模块。

module.resource为模块的绝对路径,因此这段代码判断了模块是否在node_modules下,是的话则打包到vender中。

后面一个CommonsChunkPlugin从vender中提取了运行时代码,避免每次打包vender的Hash值都发生变化致使缓存无效。

若有问题,欢迎指正。

相关文章
相关标签/搜索