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值都發生變化致使緩存無效。

若有問題,歡迎指正。

相關文章
相關標籤/搜索