filename
一、认识
output.filename
此选项决定了每个输出 bundle
的名称。这些 bundle
将写入到 output.path
选项指定的目录下。
二、语法
2.1 固定语法
在 output.path
指定目录下生成 bundle.js
module.exports = {
output: {
filename: 'bundle.js',
},
};
// 产物结构
|-- output.path/
| |-- bundle.js
在 output.path
指定目录下再指定一个目录生成 bundle.js
module.exports = {
output: {
filename: 'js/bundle.js',
},
};
// 产物结构
|-- output.path/
| |-- js
| | |-- bundle.js
2.2 占位语法
在 output.path
指定目录下生成 bundle.js
module.exports = {
output: {
filename: '[name].bundle.js',
},
};
在 output.path
指定目录下再指定一个目录生成 bundle.js
module.exports = {
output: {
filename: 'js/[name]/bundle.js',
},
};
2.3 组合语法
module.exports = {
output: {
filename: '[name].[contenthash].bundle.js',
},
};
2.4 函数语法
module.exports = {
output: {
filename: (pathData) => {
return pathData.chunk.name === 'main' ? '[name].js' : '[name]/[name].js';
},
},
};
三、占位符
文件指纹 是指打包后输出的文件名和后缀。
3.1 [ext]
资源后缀名[ext]
3.2 [name]
文件名称[name]
3.3 [path]
文件的相对路径[path]
3.4 [hash]
hash
项目每次构建都会生成一个 hash
,和整个项目有关,项目任意地方有改变就会改变。hash
会更据每次工程的内容进行计算,很容易造成不必要的hash
变更,不利于版本管理。 一般来说,没有什么机会直接使用hash
。指纹占位符生成的效率最高,成本最低,影响的范围最大,精度最低。
function createHash() {
return require('crypto').createHash('md5');
}
const entry = {
entry1: 'entry1',
entry2: 'entry2',
};
const entry1 = 'require("./module1")';
const entry2 = 'require("./module2")';
const module1 = 'module1';
const module2 = 'module2';
const hash = createHash().update(entry1).update(entry2).update(module1)
.update(module2)
.digest('hex');
console.log(hash);
根据原理可知: entry1
、entry2
、entry3
、entry4
其中任何一个文件发生改变,都会重新生成 hash
值。整个 webpack
构建的项目共用一个 hash
3.5 [folder]
文件所在的文件夹[folder]
3.6 [chunkhash]
chunkhash
和webpack
打包生成的chunk
相关。每一个entry
,都会有不同的hash
。一般来说,针对于输出文件,我们使用chunkhash
。因为webpack
打包后,最终每个entry
文件及其依赖会生成单独的一个js
文件。此时使用chunkhash
,能够保证整个打包内容的更新准确性。指纹占位符生成的效率仅次于 hash
,成本也不高,影响的范围仅次于 hash
,精度高于 hash
。
function createHash() {
return require("crypto").createHash("md5");
}
const entry = {
entry1: "entry1",
entry2: "entry2",
};
const entry1 = 'require("./module1")';
const entry2 = 'require("./module2")';
const module1 = "module1";
const module2 = "module2";
const entry1ChunkHash = createHash()
.update(entry1)
.update(module1)
.digest("hex");
console.log(entry1ChunkHash);
const entry2ChunkHash = createHash()
.update(entry2)
.update(module2)
.digest("hex");
console.log(entry2ChunkHash);
根据原理可知: chunkhash
只与自己依赖的模块有关,且一个入口一个 chunk
。比如说一个 chunk
将其中的 A
模块换成了 B
模块,那么 chunkhash
就会发生变化
3.7 [contenthash]
contenthash
和单个文件的内容相关。指定文件的内容发生改变,就会改变hash
,内容不变 hash
值不变。对于css
文件来说,一般会使用MiniCssExtractPlugin
将其抽取为一个单独的css
文件。此时可以使用contenthash
进行标记,确保css
文件内容变化时,可以更新hash
。指纹占位符生成的效率最低,成本最高,影响的范围也最小,精度最细。
function createHash() {
return require("crypto").createHash("md5");
}
const entry = {
entry1: "entry1",
entry2: "entry2",
};
const entry1 = 'require("./module1")';
const entry2 = 'require("./module2")';
const module1 = "module1";
const module2 = "module2";
const entry1File = entry1 + module1;
const entry1ContentHash = createHash().update(entry1File).digest('hex');
console.log(entry1ContentHash);
const entry2File = entry2 + module2;
const entry2ContentHash = createHash().update(entry2File).digest("hex");
console.log(entry2ContentHash);
根据原理可知: contenthash
只与自己依赖的模块的内容有关,且一个入口一个 chunk
。比如说: 一个 chunk
将其中的 A
模块换成了 B
模块,但是这两个模块内容是一致的,所以 contenthash
不会改变。
四、问题
4.1 认识 Webpack Hash 策略
在 Webpack
中,Hash
策略主要用于生成文件名中的散列值,从而实现高效缓存和文件更新检测。对于浏览器来说,一方面期望每次请求页面资源时,获得的都是最新的资源;一方面期望在资源没有发生变化时,能够复用缓存对象。这个时候,使用文件名+文件哈希值的方式,就可以实现只要通过文件名,就可以区分资源是否有更新。而webpack
就内置了hash
计算方法,对生成文件的可以在输出文件中添加hash
字段。Webpack
中的 Hash
策略有三种:
-
hash
: 在一次完整的编译过程中,Webpack
会生成一个全局唯一的hash
值,这个hash
会被所有输出的文件共享。项目每次构建都会生成一个hash
,和整个项目有关,项目任意地方有改变就会改变。hash
会更据每次工程的内容进行计算,很容易造成不必要的hash
变更,不利于版本管理。 一般来说, 没有什么机会直接使用hash
。这种策略简单,但可能导致所有文件同时失效缓存。 -
chunk hash
:Webpack
针对每个Chunk
(代码分块)生成一个hash
。一个Chunk
通常对应一个入口或通过代码分割生成的块。和webpack
打包生成的chunk
相关。每一个entry
,都会有不同的hash
。一般来说,针对于输出文件,我们使用chunkhash
。因为webpack
打包后,最终每个entry
文件及其依赖会生成单独的一个js
文件。此时使用chunkhash
,能够保证整个打包内容的更新准确性 -
content hash
: 内容Hash
是基于文件内容生成的散列值,也就是说只有当文件内容真正变化时,才会更新对应的Hash
。和单个文件的内容相关。指定文件的内容发生改变,就会改变hash
,内容不变hash
值不变。对于css
文件来说,一般会使用MiniCssExtractPlugin
将其抽取为一个单独的css
文件, 此时可以使用contenthash
进行标记,常用于提取CSS
等静态资源,能更精细地控制缓存。
4.2 Webpack 打包时 hash 码是如何生成的?
hash
生成的基本流程:
一、数据收集: 在编译和生成阶段,Webpack
会收集用于计算 hash
的数据。这些数据包括:
-
模块的源代码和经过
Loader
转译后的内容 -
模块之间的依赖关系、模块
id
、Chunk
内部的顺序信息 -
运行时代码(
Runtime
)的内容、Chunk
的元数据等
二、数据拼接与排序: 为了确保相同内容始终生成相同的 hash
,Webpack
会将相关数据以确定性(deterministic
)的顺序拼接在一起。这样,即使编译过程中其他无关因素发生变化,只要关键内容未变,hash
值就不会更新。
三、调用 hash
算法: 利用 Node.js
的 crypto
模块,Webpack
调用指定的 hash
算法(例如默认的 md4
,Webpack 5
中也可能使用更高效的算法如 xxhash64
)来对拼接后的字符串进行摘要计算,最终得到一个十六进制字符串。
四、截取与格式化: 生成的完整 hash
会根据配置要求截取部分字符(例如前 8
位、10
位等),并嵌入到输出文件名中,用于标识文件版本。这就使得只有在文件内容真正发生变化时,输出文件名中的 hash
才会改变,从而实现浏览器的高效缓存。