webpack搭建React項目(5)

chunk 和 bundle

Concepts - Bundle vs Chunkcss

常常讓人摸不着頭腦的是 chunk 和 bundle 這兩個概念。chunk,翻譯過來就是大塊,也就是代碼塊;而 bundle 則是束,包的意思。從 webpack 給出的術語表中是這麼解釋的:前端

Chunk: This webpack-specific term is used internally to manage the bundling process. Bundles are composed out of chunks, of which there are several types (e.g. entry and child). Typically, chunks directly correspond with the output bundles however, there are some configurations that don't yield a one-to-one relationship.webpack

chunk 在 webpack 中用於內部打包的過程;bundle 由 chunk 組成,通常來講,chunk 和 bundle 是對應的,可是也可能經過配置改變它們一一對應的關係。git

Bundle: Produced from a number of distinct modules, bundles contain the final versions of source files that have already undergone the loading and compilation process.github

bundle 由不一樣的模塊組成,包含已經進行加載和通過編譯的源文件的最終輸出文件。web

webpack 文檔的解釋很模糊,chunk 實際上是 code splitting 中的概念,當使用到 code splitting 將 bundle 拆分出多個 chunk 就能體會到 chunk 和 bundle 的區別了。瀏覽器

output

webpack 中用來管理輸出的配置項主要就是output配置項,output可選的屬性仍是不少的,經常使用的有如下部分:緩存

  • filename:指定每一個打包輸出 bundle JS 的名稱,若是是隻指定entry是一個入口文件,那麼默認也只會生成一個名稱爲main.js的 bundle 文件。在代碼拆分的時候,須要經過 [hash]來指定不一樣的文件名。
  • chunkFilename:指定非入口 chunk 的名稱,默認是使用 chunk 的 id 來指定,即[id].js
  • hashSalt:hash 加鹽是一種密碼學中的手段,對須要進行 hash 運算的內容在任意固定位置插入特定的字符串來讓加鹽後的散列結果和沒有加鹽的結果不相同。
  • hotUpdateChunkFilename:自定義熱更新 chunk 的文件名,默認是[name].[hash].hot-update.js,這裏的[name]是上面指定的filename,例如:

  • scriptType:指定<script>標籤插入到頁面中的type屬性,默認是什麼都不指定,對於<script>標籤來講,type屬性爲空,則會將文件看做 JS
  • path:指定整個項目打包輸出的 bundle 的目錄,默認是項目根目錄的dist文件夾
  • publicPath:開發環境通常用不到這個配置,若是在使用 WDS 的時候,同時指定publicPath,就表示提供給 WDS 的文件都來自於publicPath目錄;可是生產環境下可能用來配置 CDN 的路徑前綴
  • sourceMapFilename:僅在 devtool 設置爲 'source-map' 時有效,也就是生成的 source map 的文件名,默認狀況下是和 bundle 在同一個目錄中
  • ecmaVersion:控制生成代碼的 ES 版本,在 webpack4 中這個值是5,在 webpack5 中,這個值是6,也就是容許 ES6 代碼存在
  • compareBeforeEmit:在打包輸出文件以前,檢查文件在目錄中是否已經存在,若是存在就再也不新寫入一個相同的文件,默認是true
  • iife:添加 IIFE 外層包裹的括號,默認是true
  • module:默認是true,即容許輸出的 JavaScript 文件做爲模塊類型
  • pathinfo:在生產環境下默認是true,即引入「所包含模塊信息」的相關注釋;在開發環境下默認是false,且建議是false

filename 和 chunkFilename

webpack - 緩存網絡

從概念解釋上來講,output.filename指定的是主 bundle 的文件名稱;output.chunkFilename指定的是 chunk 的文件名稱,若是爲 webpack 只指定了一個入口entry,那麼output.chunkFilename是沒啥用的,只有代碼拆分的時候指定多個 chunk,這個配置項才能體現出來,拆分出的 chunk 若是找不到output.chunkFilename就會繼而使用output.filename做爲 chunk 文件名。ide

web 開發中常常遇到的一個問題就是瀏覽器對資源的緩存,致使發佈的新的 JS 文件沒法生效;過去解決方式通常是在 JS 的文件名後面添加一串不重複的版本號。在工程化的前端項目裏,顯然沒法經過手動修改文件名來完成替換。

緩存是有用的,經過代碼拆分,咱們能夠作到將一些不會常常改變的核心代碼抽成一個 chunk 進行打包,並賦予一個長期緩存來解決瀏覽器重複請求網絡去加載資源的問題。對於不常更改的 chunk,咱們但願每次打包它們的名稱都是固定的,而對於常常修改的 chunk,須要根據內容去每次生成一個惟一的 chunk 名稱來保證更新客戶端的緩存。

一般 webpack 會爲每個模塊分配一個惟一的模塊標識符 module identifier,這個 id 是一個 Int 類型的數字,而且一般從0開始,依據生產的 chunk 依次遞增。

webpack 可使用一種稱爲 substitution(可替換模板字符串) 的方式,經過使用內容散列(content hash)替換在output.filename或output.chunkFilename配置的模板字符串來做爲輸出 bundle 文件的名稱,這樣在文件內容修改時,會計算出新的 hash,瀏覽器會使用新的名稱加載文件,從而使緩存無效。

具體可使用的模板字符串見—— loader-utils.interpolateName

模板字符串 含義
[hash] 根據模塊 id 生成的 hash
[contenthash] 根據文件內容生成的 hash,每一個文件資源都不相同
[chunkhash] 根據每一個 chunk 內容生成的 hash
[name] module name,若是 module 沒有名稱,則會使用其 id 做爲名稱
[id] module identifier,默認是根據模塊引入的順序,從0開始的整數
[query]
[function]

在上面的模板字符串中存在三種 hash,默認三種 hash 的長度都是20個字符長度,能夠經過加 length 的方法[xxxhash::<length>]指定 hash 的長度。而且若是開發環境使用 WDS,那麼[contenthash]沒法是用於開發環境的。

第一種[hash],須要注意的是它是根據模塊 id 生成的,因此每一個 chunk 獲得的值都是同樣的,在指定代碼拆分之後,對其作了測試,能夠看到兩個 chunk 的 hash 都是同樣的。

若是修改其中一個 chunk 的模塊代碼,全部 chunk 的 hash 值都會發生變化,因此使用[hash]是不穩定的,達不到上面咱們說的目的。

[chunkhash]是根據每一個 chunk 內容生成的 hash 值,這種狀況在有些時候它是穩定的,我在修改單獨入口文件的模塊代碼時,並未影響其它 chunk 的 hash 值。

可是當 chunk 內 CSS 和 JS 混雜的時候,例如在 React 中import一個單獨的 CSS 文件,這是很常見的事,若是對output.filename使用了[chunkhash],而對導出的 CSS 也使用了[chunkhash],那麼 JS 和 CSS 獲得的 hash 值將是同樣的,這時候 JS 和 CSS 的變化會相互影響。例以下面的配置致使的結果是 JS 主 bundle 的 hash 值和 CSS 的 hash 值始終同樣。

module.exports = {  output: {filename: isProduction
      ? 'static/js/[name].[chunkhash].js'  : 'static/js/bundle.js',path: path.resolve(__dirname, 'build'),
  },  plugins: [
    isProduction &&      new MiniCssExtractPlugin({filename: 'static/css/[name].[chunkhash].css',
      }),
    ,
  ],
};複製代碼

而若是僅對 JS 使用[chunkhash],而 CSS 使用[contenthash],那麼 CSS 發生變化,JS 的 hash 名稱同樣也會變。

module.exports = {  output: {filename: isProduction
      ? 'static/js/[name].[chunkhash].js'  : 'static/js/bundle.js',path: path.resolve(__dirname, 'build'),
  },  plugins: [
    isProduction &&      new MiniCssExtractPlugin({filename: 'static/css/[name].[contenthash].css',
      }),
    ,
  ],
};複製代碼

至於[contenthash]則是根據具體的模塊內容生成的 hash 值,它能檢測細微層次 module 的變化,因爲 chunk 包含 module,[contenthash]是爲單個 module 準備的,在使用[contenthash]之後,chunk 中的 CSS 和 JS 模塊不會相互影響。

最後對於進行 code splitting 的項目,建議以下的配置,[contenthash]還能夠附加像[contenthash:10]這樣的形式來決定生成的 hash 字符串的長度。

module.exports = function(env) {  const isDevelopment = env.NODE_ENV === 'development';  const isProduction = env.NODE_ENV === 'production';  return {mode: isProduction ? 'production' : isDevelopment && 'development',output: {      filename: isProduction
        ? 'static/js/[name].[contenthash].js': 'static/js/bundle.js',      chunkFilename: isProduction
        ? 'static/js/[name].[contenthash].chunk.js': 'static/js/[name].chunk.js',
    },plugins: [
      isProduction &&new MiniCssExtractPlugin({          filename: 'static/css/[name].[contenthash].css',          chunkFilename: 'static/css/[name].[contenthash].chunk.css',
        }),
    ],
  };
};複製代碼

clean-webpack-plugin

當使用[contenthash]替換 chunk 名稱的時候,對於修改過的 chunk,每次都會生成一個具備新的 chunk 名的 chunk,而舊的 chunk 會依然保留在output.path文件夾中,這些垃圾文件會隨着每次 build 愈來愈多。

clean-webpack-plugin是負責清理 build 文件夾的插件,默認狀況下,這個插件會清空在output.path文件夾裏的全部文件,以及每次成功重建後全部未使用的 webpack 靜態資源。如今這個插件已經到了 V3.0 版本。

yarn add clean-webpack-plugin -D複製代碼
// 須要注意這裏要帶括號const { CleanWebpackPlugin } = require('clean-webpack-plugin'); //清理build文件夾module.exports = {  plugins: [isProduction && new CleanWebpackPlugin()],
};複製代碼
相關文章
相關標籤/搜索