module、chunk、bundle都是webpack中的術語。那麼他們究竟是什麼呢?對于這3個名詞感覺他們都在說打封包件,但是具體細節的差別呢?希望通過本文大家對此有細緻的了解。
一、術語的解釋
1. module是什麼?
官方概念:Module 是離散功能塊,相比于完整程式提供了更小的接觸面。精心編寫的子產品提供了可靠的抽象和封裝界限,使得應用程式中每個子產品都具有條理清楚的設計和明确的目的。
module其實就是一個檔案或者檔案内通過import等方式引用代碼塊或第三方等均可認為是一個module,也就是說任何一個可以被導入導出的檔案都是一個子產品。
2. chunk是什麼?
官方概念:Chunk 此 webpack 特定術語在内部用于管理捆綁過程。輸出束(bundle)由塊組成,其中有幾種類型(例如 entry 和 child )。通常,塊 直接與 輸出束 (bundle)相對應,但是,有些配置不會産生一對一的關系。
當我們寫的 module 源檔案傳到 webpack 進行打包時,webpack 會根據檔案引用關系生成 chunk 檔案,webpack 會對這個 chunk 檔案進行一些操作。
3. bundle是什麼?
官方概念:bundle 由許多不同的子產品生成,包含已經經過加載和編譯過程的源檔案的最終版本。
webpack 處理好 chunk 檔案後,最後會輸出 bundle 檔案,這個 bundle 檔案包含了經過加載和編譯的最終源檔案,是以它可以直接在浏覽器中運作。通常我們會弄混這兩個概念,以為Chunk就是Bundle,Bundle就是我們最終輸出的一個或多個打封包件。大多數情況下,一個Chunk會生産一個Bundle。
二、案例示範
從定義來說:
- “子產品”(module)的概念大家都比較熟悉,如 CommonJS 子產品、AMD、ES6 Modules 子產品。
- chunk 表示打包的時候産生的子產品,由他來組成 bundle。
- 打包完成的源代碼。
我們現在就隻建立一個webpack配置,步驟如下:
1.建立一個空檔案夾,并且在目前檔案夾下打開cmd。
2.npm init -y 生成package.json。
3.執行 npm i webpack webpack-cli -D, 安裝webpack的包。
4.建立src,在src内部建立index.html、index.js、index.css、utils.js、common.js,并且編寫内部代碼。
5.在項目根目錄建立 webpack.config.js。
6.直接在cmd中運作 webpack。
1.目錄結構
src
├── index.css
├── index.html # 這個是 HTML 模闆代碼
├── index.js
├── common.js
└── utils.js
webpack.config.js
package.json
2.各檔案代碼内容
index.html
<body>
<div class="box">Hello</div>
</body>
index.css
body {
background-color: pink;
}
.box {
font-size: 24px;
font-weight: bold;
}
index.js
import "./index.css";
const { log } = require("./common");
log("webpack");
common.js
module.exports = {
log: (msg) => {
console.log(msg);
},
};
utils.js
export function add(x, y) {
return x + y;
}
3.webpack的配置
// 在配置檔案中,使用的子產品化規範是 commonjs
const path = require("path");
// 引入編譯html的插件
const HTMLWebpackPlugin = require("html-webpack-plugin");
// 引入抽離css檔案的插件
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const options = {
stats: {
chunks: true,
ids: true,
hash: true,
},
optimization: {
chunkIds: "natural",
},
// 模式
// development 開發模式 production 生産模式
mode: "development",
// 入口子產品的聲明
entry: {
index: "./src/index.js",
utils: "./src/utils.js",
},
// 輸出目錄
output: {
// ./dist
path: path.resolve(__dirname, "dist"),
// 輸出 index.js 和 utils.js
filename: "./js/[name].bundle-[hash].js",
// 每次編譯的時候把上一次的内容給清空
clean: true,
},
// loader - 可以讓webpack解析非js的其他子產品
module: {
// 解析規則
rules: [
{
test: /.css$/,
use: [
MiniCssExtractPlugin.loader,
"css-loader", // css-loader 負責解析 CSS 代碼, 處理 CSS 中的依賴
],
},
{
test: /\.js$/,
// 排除項 - 也是正則
exclude: /node_modules/,
// 使用的loader - 如果是配置項,則寫成對象
use: {
// 指定loader
loader: "babel-loader",
// 更好的在其他的浏覽器相容es6進階文法
options: {
// 預設方案和細節聲明 - 二維數組
presets: [
[
"@babel/preset-env",
{
// 隻打包使用的ES6新API的實作代碼
useBuiltIns: "usage",
// 指定core-js的版本号為2
corejs: { version: 2 },
},
],
],
},
},
},
],
},
plugins: [
// 編譯html的插件
new HTMLWebpackPlugin({
// 指定模闆存放的路徑
template: "./src/index.html",
// 在body中注入編譯好的檔案
inject: "body",
// 每一次編譯的bundle.js 帶一些哈希,防止緩存
hash: true,
// 關于html的壓縮處理
minify: {
// 移除屬性中的雙引号
removeAttributeQuotes: true,
// 移除注釋
removeComments: true,
// 去除換行和空格
collapseWhitespace: true,
},
}),
// 用 MiniCssExtractPlugin 抽離出 css 檔案,以 link 标簽的形式引入樣式檔案
new MiniCssExtractPlugin({
filename: "index.bundle-[hash].css", // 輸出的 css 檔案名為 index.css
}),
],
};
module.exports = options;
4.運作webpack
我們可以看出,index.css 和 common.js 在 index.js 中被引入,打包生成的 index.bundle-[hash].css 和 index.bundle-[hash].js 都屬于 chunk 0,utils.js 因為是獨立打包的,它生成的 utils.bundle-[hash].js 屬于 chunk 1。
一般來說一個 chunk 對應一個 bundle,比如上圖中的utils.js -> chunk 1 -> utils.bundle.js;但也有例外,比如說上圖中,我就用MiniCssExtractPlugin從 chunks 0 中抽離出了index.bundle.css檔案。
三、3種hash值詳解
1.webpack中的三種hash分别是:
- hash:全局hash
- chunkhash:分組hash
- contenthash:内容hash
2.hash
webpack.config.js輸出檔案名規則修改為hash時。
output: {
// ./dist
path: path.resolve(__dirname, "dist"),
// 輸出 index.js 和 utils.js
filename: "./js/[name].bundle-[hash].js",
// 每次編譯的時候把上一次的内容給清空
clean: true,
},
plugins: [
// 用 MiniCssExtractPlugin 抽離出 css 檔案,以 link 标簽的形式引入樣式檔案
new MiniCssExtractPlugin({
filename: "index.bundle-[hash].css", // 輸出的 css 檔案名為 index.css
}),
],
預設的是hash,直接運作打包webpack,我們看看我們打包後的檔案是什麼樣的。
可以看到,所有檔案的檔案名hash值都是一緻的,那我們現在改一下index.css這個檔案。
body {
/* background-color: pink; */
background-color: blue;
}
運作打包webpack,我們看看我們打包後的檔案是什麼樣的。
可以看出,修改一個檔案,所有檔案的hash值跟着變。
結論:牽一發動全身,隻改了一個index.css,會導緻打包後所有檔案的hash值都改變。是以當打包名稱設定為hash時,整個項目檔案是一緻的,修改其中一個會導緻所有跟着一起改。
2.chunkhash
webpack.config.js輸出檔案名規則修改為chunkhash
output: {
// ./dist
path: path.resolve(__dirname, "dist"),
// 輸出 index.js 和 utils.js
filename: "./js/[name].bundle-[chunkhash].js",
// 每次編譯的時候把上一次的内容給清空
clean: true,
},
plugins: [
// 用 MiniCssExtractPlugin 抽離出 css 檔案,以 link 标簽的形式引入樣式檔案
new MiniCssExtractPlugin({
filename: "index.bundle-[chunkhash].css", // 輸出的 css 檔案名為 index.css
}),
],
預設的是chunkhash,直接運作打包webpack,我們看看我們打包後的檔案是什麼樣的。
我們可以看出,chunkhash值會根據入口檔案的不同而分出兩個陣營:
- index.js、index.css一個陣營
- utils.js一個陣營
那我們現在照樣修改一下index.css:
body {
/* background-color: pink; */
/* background-color: blue; */
background-color: yellow;
}
重新運作webpack打包看看:
可以看出,index.css修改後會影響index.css、index.js的chunkhash值。
結論:當規則為chunkhash時,打包後的chunkhash值會根據入口檔案的不同而不一樣,當某個入口檔案修改後重新打包,會導緻本入口檔案關聯的所有檔案的chunkhash值都修改,但是不會影響到其他入口檔案的chunkhash值。
3.contenthash
webpack.config.js輸出檔案名規則修改為contenthash
output: {
// ./dist
path: path.resolve(__dirname, "dist"),
// 輸出 index.js 和 utils.js
filename: "./js/[name].bundle-[contenthash].js",
// 每次編譯的時候把上一次的内容給清空
clean: true,
},
plugins: [
// 用 MiniCssExtractPlugin 抽離出 css 檔案,以 link 标簽的形式引入樣式檔案
new MiniCssExtractPlugin({
filename: "index.bundle-[contenthash].css", // 輸出的 css 檔案名為 index.css
}),
],
預設的是contenthash,直接運作打包webpack,我們看看我們打包後的檔案是什麼樣的。
可以看到,每個檔案的contenthash值都不一樣,每個檔案的contenthash值都是根據自身的内容去生成的,那我們現在修改一下index.css:
body {
/* background-color: pink; */
/* background-color: blue; */
/* background-color: yellow; */
background-color: red;
}
重新運作webpack打包看看:
可以看出,index.css修改後隻會影響index.css的contenthash值,也就是自己的contenthash值。
結論:當規則為contenthash時,每個檔案的contenthash值都是根據自身内容而生成,當某個檔案内容修改時,打包後隻會修改其本身的contenthash值,不會影響其他檔案的contenthash值。
總結
同一份邏輯代碼中module,chunk 和 bundle 就是在不同轉換場景下的三個術語,我們直接寫出來的是 module,webpack 處理時是 chunk,最後生成浏覽器可以直接運作的 bundle。
當打包名稱設定為hash時,整個項目檔案是一緻的,修改其中一個會導緻所有跟着一起改。當規則為chunkhash時,打包後的chunkhash值會根據入口檔案的不同而不一樣,當某個入口檔案修改後重新打包,會導緻本入口檔案關聯的所有檔案的chunkhash值都修改,但是不會影響到其他入口檔案的chunkhash值。當規則為contenthash時,每個檔案的contenthash值都是根據自身内容而生成,當某個檔案内容修改時,打包後隻會修改其本身的contenthash值,不會影響其他檔案的contenthash值。