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值。