Webpack中hash、chunkhash和contenthash主要与浏览器缓存行为有关,本节对这三者进行讲解。
一、浏览器缓存
在讲hash之前,先简单讲解一下浏览器的缓存知识,有助于大家理解hash。
当浏览器访问一个html页面时,html页面会加载JS、CSS和图片等外部资源,这需要花费一定的下载时间。
如果页面上有一些外部资源是长时间不变的,例如jQuery.js或商标图片等,那么我们可以把这部分资源存储在磁盘本地,这个就是缓存。
在下一次访问该页面的时候,直接从磁盘本地取回缓存的jQuery.js或商标图片等,这样就不需要花费时间下载了。
那么浏览器怎么知道该资源是要从磁盘本地取,还是去网络服务器请求下载呢?
我们可以在浏览器第一次访问该页面的时候,服务器对于需要缓存在用户磁盘本地的资源附加表示资源缓存有效期的响应头,例如cache-control等。
浏览器获得资源后,只要同名的资源在缓存有效期内,就会把该资源一直缓存在磁盘本地。于是下一次访问该页面的时候,对于相同的资源,不会再去请求服务器的资源,而是直接使用磁盘本地的。
我们在服务器上可以把缓存的有效期设置为几天、几个月甚至几年,使该资源长时间缓存在本地。
但是,如果我们的资源内容变化了,例如jQuery.js里的代码变动了,不想使用本地缓存的了,该怎么办?
一个办法就是给jQuery.js一个独特的命名,例如jQuery-8af331g2.js。只要jQuery-8af331g2.js代码内容没变,我们html上就引入名字是jQuery-8af331g2.js脚本。
<script src="jQuery-8af331g2.js"></script>
今后只要JS代码内容不变,我们在初次访问页面后,就可以一直使用缓存在本地的jQuery-8af331g2.js
如果代码内容变化了,浏览器再使用jQuery-8af331g2.js就会出问题了。那么我们就用一个新的名字,例如jQuery-3b551ac6.js,我们把html修改成引入名字是jQuery-3b551ac6.js的脚本。
<script src="jQuery-3b551ac6.js"></script>
这样浏览器发现本地没有缓存该名字的JS文件,就回去服务器请求该资源jQuery-3b551ac6.js,保证在资源的准确性。
只要资源变动了,我们就需要用一个新的类似3b551ac6这种的名字。那么我们如何保证每次变动后新的3b551ac6这类名字都是唯一的呢?
这就引出了我们这节要讲的hash知识,我们在上一节Webpack出口outputhttps://www.jiangruitao.com/webpack/output/已简单使用过hash。
二、Webpack与hash算法
hash,中文译作哈希或散列。接触过数据结构与算法的话,会了解一点hash算法。
我们在这里不做过多讲解,只讲一下Webpack里hash算法是怎么一回事。
在使用Webpack对构建的时候,Webpack会根据所有的文件内容计算出一个特殊的字符串。只要有文件的内容变化了,Webpack就会计算出一个新的特殊字符串。
Webpack在根据文件内容计算出一个特殊字符串的时候,使用的就是hash算法,这个特殊字符串一般叫做hash值。
我们一般取计算出的特殊字符串的前八位作为文件名的一部分,因为hash算法计算出的前八位基本可以保证唯一性了。
在Webpack里,我们通常用[hash:8]这种形式表示取hash值的前八位,例如在Webpack配置文件中,我们用 filename: 'jQuery-[hash:8].js'。
三、webpack中hash、chunkhash和contenthash的区别
Webpack通过对文件进行hash算法获得的hash值,除了有hash,还有chunkhash和contenthash,那么这三者有什么不同呢?
首先,hash、chunkhash和contenthash这三者都是根据文件内容计算出的hash值,只是它们所计算的文件不一样。
hash是根据打包中所有的文件计算出的hash值。在一次打包中,所有出口文件的filename获得的[hash]都是一样的。
chunkhash是根据打包过程中当前chunk计算出的hash值。如果Webpack配置是多入口配置,那么通常会生成多个chunk,每个chunk对应的出口filename获得的[chunkhash]是不一样的。这样可以保证打包后每一个JS文件名都不一样(这么说不太严谨,但有助于理解)。
我们来看一个例子,配套github仓库 https://github.com/jruit/webpack-tutorial 的webpack2-5
Webpack配置文件如下,第一次打包filename取值为'[name]-[hash:8].js',第二次为'[name]-[chunkhash:8].js'。两次打包后控制台显示结果如下方两图所示
const path = require('path'); module.exports = { entry: { app1: './a.js', app2: './b.js', app3: './c.js', }, output: { path: path.resolve(__dirname, ''), filename: '[name]-[hash:8].js' // filename: '[name]-[chunkhash:8].js' }, mode: 'none' };
contenthash有点像chunkhash,是根据打包时CSS内容计算出的hash值。一般在使用提取CSS的插件的时候,我们使用contenthash。例如下面的配置,我们生成的CSS文件名可能会是main.3aa2e3c6.css。
plugins:[ new miniExtractPlugin({ filename: 'main.[contenthash:8].css' }) ]
小结
Webpack中hash、chunkhash和contenthash主要与浏览器缓存行为有关。浏览器在初次请求服务端资源的时候,服务器给JS、CSS和图片等资源一个较长的缓存时间,我们通过给资源名称增加hash值来控制浏览器是否继续使用本地缓存。hash、chunkhash和contenthash这三者都是根据文件内容计算出的hash值,[hash]是根据全部参与打包的文件计算出来的,[chunkhash]是根据当前打包的chunk计算出来的,[contenthash]是CSS文件的。
作者写的很细,babel教程受益匪浅,现在持续跟进webpack教程,希望作者一直产出优秀的教程文章!!
感谢赞誉,会越写越好的
我也一直在学习,作者写的挺好,学到了一些。
加油老哥 !!!
真的非常棒!深入浅出,让我对知识有了更系统化的了解,非常感谢~希望作者老哥可以一直产出优质的教程文章,加油!
写得很好