webpack文件指纹中hash、chunkhash和contenthash的区别
webpack文件指纹中hash、chunkhash和contenthash的区别
在webpack打包中,通常哈希的种类有项目hash、chunkhash、contenthash。而哈希一般是结合CDN缓存来使用的。通过webpack构建之后,生成对应文件名自动带上对应的MD5值。如果文件内容改变的话,那么对应文件哈希值也会改变,对应的HTML引用的URL地址也会改变,触发CDN服务器从源服务器上拉取对应数据,进而更新本地缓存。
1. 文件指纹
文件指纹可以由以下占位符组成:
占位符名称 | 含义 |
---|---|
ext | 资源后缀名 |
name | 文件名称 |
path | 文件的相对路径 |
hash | 每次webpack构建时生成一个唯一的hash值 |
chunkhash | 根据chunk生成hash值,来源于同一个chunk,则hash值就一样 |
contenthash | 根据内容生成hash值,文件内容相同hash值就相同 |
2. hash、chunkhash和contenthash的区别
hash
hash:一整个项目,一次打包,只有一个hash值
chunkhash
什么是chunk?
从入口entry出发,到它的依赖,以及依赖的依赖,依赖的依赖的依赖,等等,一直下去,所打包构成的代码块(模块的集合)叫做一个chunk,也就是说,入口文件和它的依赖的模块构成的一个代码块,被称为一个chunk。 所以,一个入口对应一个chunk,多个入口,就会产生多个chunk 所以,单入口文件,打包后chunkhash和hash值是不同的,但是效果是一样的
什么是chunkhash?
每个chunk打包后,会产生的哈希,叫做chunkhash
举个例子:
错误示例:
// webpack.config.js
module.exports = {
entry: {
entry1: './src/entry1.js',
entry2: './src/entry2.js',
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.[hash:8].js' // 这样是错误的,因为hash,整个项目只有一个,但是我们这边入口有两个,打包生成的文件肯定也是两个,这两个肯定是不一样的,所以如果用hash的话,两个文件名就一样的,这是不对的,所以,可以换成'bundle.[chunkhash:8].js'
},
}
正确示例:
// webpack.config.js
module.exports = {
entry: {
entry1: './src/entry1.js',
entry2: './src/entry2.js',
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.[chunkhash:8].js'
},
}
contenthash
这个哈希只跟内容有关系,内容不变,哈希值不变。
举个例子:
// a文件
require('./b')
console.log(1)
// b文件
console.log(2)
console.log(3)
// 那么其实内容就是下面三句代码,contenthash是对下面的内容进行哈希提取
console.log(2)
console.log(3)
console.log(1)
----------------------------------------------------------------------
// 那么其实,跟以下是一样的:
// a文件
require('./b')
console.log(3)
console.log(1)
// b文件
console.log(2)
// 那么这时候内容也是下面三句代码。contenthash也是对下面三句代码进行哈希提取
console.log(2)
console.log(3)
console.log(1)
hash、chunkhash、contenthash三个哈希应用场景举例见下文!!!
3. 各类哈希是如何生成的?
hash是如何生成的,这里指得是对某个文件生成一个hash?
let crypto = require('crypto');
let content = fs.readFileSync('a.jpg'); // 读取文件
let hash = crypto.createHash('md5').update(content).digest('hex').slice(0, 10); // 生成10位hash
webpack中的hash是如何生成的?
let crypto = require('crypto');
// 伪代码
let entry = {
entry1: 'entry1', // 值'entry1'代表entry1这个文件,伪代码
entry2: 'entry2', // 值'entry2'代表entry2这个文件,伪代码
}
let entry1 = ’require("./depModule1")'; // 代表entry1中引入了depModule1,伪代码
let entry2 = ’require("./depModule2")'; // 代表entry2中引入了depModule2,伪代码
let depModule1 = 'depModule1'; // 代表depModule1的内容是depModule1
let depModule2 = 'depModule2'; // 代表depModule2的内容是depModule2
let hash = crypto.createHash('md5')
.update(entry1)
.update(entry2)
.update(depModule1)
.update(depModule2)
.digest('hex');
// 这就说明,webpack的hash是对每个依赖模块进行update得到的,只要任意依赖的模块由改变,那么hash值就会改变
chunkhash是如何生成的?
let crypto = require('crypto');
// 伪代码
let entry = {
entry1: 'entry1', // 值'entry1'代表entry1这个文件,伪代码
entry2: 'entry2', // 值'entry2'代表entry2这个文件,伪代码
}
let entry1 = ’require("./depModule1")'; // 代表entry1中引入了depModule1,伪代码
let entry2 = ’require("./depModule2")'; // 代表entry2中引入了depModule2,伪代码
let depModule1 = 'depModule1'; // 代表depModule1的内容是depModule1
let depModule2 = 'depModule2'; // 代表depModule2的内容是depModule2
let chunkhash_of_entry1 = crypto.createHash('md5')
.update(entry1)
.update(depModule1)
.digest('hex');
let chunkhash_of_entry2 = crypto.createHash('md5')
.update(entry2)
.update(depModule2)
.digest('hex');
// 这就说明,webpack的chunkhash是对每个入口自己的依赖模块进行update得到的,每个入口对应一个chunkhash,入口文件和依赖有所改变,那么这个入口对应的chunkhash就会改变
contenthash是如何生成的?
let crypto = require('crypto');
// 伪代码
let entry = {
entry1: 'entry1', // 值'entry1'代表entry1这个文件,伪代码
entry2: 'entry2', // 值'entry2'代表entry2这个文件,伪代码
}
let entry1 = ’require("./depModule1")'; // 代表entry1中引入了depModule1,伪代码
let entry2 = ’require("./depModule2")'; // 代表entry2中引入了depModule2,伪代码
let depModule1 = 'depModule1'; // 代表depModule1的内容是depModule1
let depModule2 = 'depModule2'; // 代表depModule2的内容是depModule2
let entry1File = entry1 + depModule1;
let contenthash_of_entry1 = crypto.createHash('md5')
.update(entry1File)
.digest('hex');
4. 各类哈希的区别,或,各类哈希如何选择?(面试题)
- hash、chunkhash、contenthash,首先生成效率越来越低,成本越来越高,影响范围越来越小,精度越来越细。
- hash是一整个项目,一次打包,只有一个hash值,是项目级的
- chunhash是从入口entry出发,到它的依赖,以及依赖的依赖,依赖的依赖的依赖,等等,一直下去,所打包构成的代码块(模块的集合)叫做一个chunk,也就是说,入口文件和它的依赖的模块构成的一个代码块,被称为一个chunk。
- contenthash是哈希只跟内容有关系,内容不变,哈希值不变。与chunkhash的区别可以举上面contenthash的例子,同时可以说明contenthash跟内容有关,但是chunkhash会考虑很多因素,比如模块路径、模块名称、模块大小、模块id等等。
5. 各类哈希的应用
- 题目一:以下能否打包成功,如果不能,是什么原因?
module.exports = {
entry: {
main: './src/index.js',
vendor: ['lodash']
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[hash].js'
}
}
不能,因为这是多入口文件打包,会生成多个文件,但是由于hash是根据项目生成的,一个项目对应一个hash,所以会导致生成的文件同名,webpack不允许这么做,所以不能打包成功。
- 题目二:以下能否打包成功,如果不能,是什么原因?
module.exports = {
entry: {
main: './src/index.js',
vendor: ['lodash']
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].[hash].js'
}
}
能,因为虽然是多入口文件打包,会生成多个文件,并且即便hash一样,由于filename是根据name和hash共同决定的,name是entry的key,key不同,所以生成的文件不同,所以可以打包成功。
- 题目三:上面的打包方式是否存在缺点,如果存在,则应该怎么优化?
module.exports = {
entry: {
main: './src/index.js', // 这里有引入a.css
vendor: ['lodash']
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].[chunkhash].js'
},
plugin: [
new MiniCssExtractPlugin({
filename:'css/[chunkhash].css'
}),
]
}
存在缺陷。
首先,output中的chunkhash是没有问题的,问题在于plugin的css的chunkhash。
这里css应该使用contenthash。原因如下:
由于已知入口文件main引入了a.css,那么插件MiniCssExtractPlugin会将css从入口文件main中抽离出来打包成单独的css文件,由于是从入口文件main中抽离出来的,所以main的chunkhash和css的chunkhash是一样的,因为chunkhash是根入口文件和入口文件的依赖相关。
这就存在了一个问题:当我main中的js代码发生了变化,那么这个chunkhash就变了,这样css的哈希也就跟着变了,但事实上,css并没有做修改,所以不需要变化哈希值。
所以,这里的css的哈希就可以使用contenthash,根据css的内容来变化,内容变了哈希就变,内容不变哈希就不变。
6. 其他问题:
问题一:一个入口文件就只能生成一个文件吗?
不是的,main可以生成两个文件,比如main.js和main.css,css是用插件从main.js中抽离出来的
问题二:生成hash的时候,对同一个内容,多次update,结果一样吗?
不一样。.update(a).update(b)相当于.update(a+b)
let hash1 = crypto.createHash('md5').update(content).digest('hex') let hash2 = crypto.createHash('md5').update(content).update(content).digest('hex')
console.log(hash1 !== hash2) // true
let hashA = crypto.createHash('md5').update('123').digest('hex') let hashB = crypto.createHash('md5').update('1').update('2').update('3').digest('hex')
console.log(hashA === hashB) // true
7. 参考文章
文章转自13. 重学webpack--一文彻底了解hash、chunkhash和contenthash的区别_wx5c04c8a88fd20的技术博客_51CTO博客,如有侵权,请联系删除
转载自:https://juejin.cn/post/7142678993479663630