前言: webpack的hash值是持续缓存和版本控制重要的一部分,webpack中常见到的hash有[hash], chunkhash,contenthash,下面会详细解读下这个三个hash值及如何实现较成熟的打包策略
一.hash-chunkhash-contenthash
[hash]: 根据所有chunk 生成的hash值,也就是每次打包的时候会生成统一唯一的[hash]值,并且某个chunk修改后,hash值都会变,不利于持续缓存
一般的配置:
字体文件,图像文件用file-loader或者url-loader生成的[hash](url-loader是file-loader的加强版,可以通过设置limit的大小,把小图标转化成base64)
css 用extract-text-webpack-plugin插件根据样式文件生成的contenthash值
js 用webpack生成的chunkhash值
二.持续缓存和版本控制的实现
webpack需要根据chunk的有更改内容才生成新的hash值,并且css,js,图片,字体各自的更改都不会相互影响。
1.合理划分chunk
- ui.js
存放一些第三方ui库; - vendor.js
基础库和工具,如:
'babel-polyfill',
'core-js',
'react',
'react-dom',
'react-router',
'react-router-dom',
'whatwg-fetch' - 业务.js
业务模块代码
2.合理选取webpack插件
提取公共模块后,我们需要保证每次业务模块修改后,生成的新的hash的js文件引用公用模块时不会出错,比如找不到对应模块的代码。
- CommonsChunkPlugin插件
提取公共代码最原始的方式是用webpack的CommonsChunkPlugin插件
new webpack.optimize.CommonsChunkPlugin({
name: vendor,
minChunks: Infinity
}),
但是这样存在很严重的问题就是每次业务模块更改时,vendor的hash值都会变化,这样造成的结果就是浏览器无法使用缓存的vendor,因此我们还得进一步改造
为解决这个问题曾经有人推荐使用webpack-md5-hash, 但是这个插件有坑,会出现这种问题github.com/erm0l0v/web…
原因:webpack-md5-hash 的原意是为了解决vendor不随业务代码修改而改变hash值,但是结果确实是hash不变了,但是vendor.js里面的模块id却变了,比如业务模块修改前引用了vendor中id为1的模块,修改后打包的vendor原来id为1的模块,现在id变成了2,由于hash值没有变,所以浏览器引用的vendor是来自缓存,导致在vendor里找不到id为2的模块
其实我们最终的目的就是保证vendor的hash值不随业务代码变更而变更,并且正确记录模块之间的引用映射。下面有三种主流的解决方式:
NamedModulesPlugin插件
plugins: [
new webpack.NamedModulesPlugin(),
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
minChunks: Infinity
})]这个插件是根据chunk在项目的相对路径命名模块id,只要vendor中的模块在根目录下的路径不变,但是路径字符会导致文件大小剧增
DllPlugin 插件
这种方式是把vendor和业务模块分开编译,先编译vendor,然后会生成一个映射表(相对路径对应的id),然后在编译业务模块的时候通过DllReferencePlugin 插件引入这个映射表,从而实现到vendor的映射。new webpack.DllReferencePlugin({
context: '',
manifest: require('./dist/vendor.json')
})这种方式存在的问题主要是需要分开打包,步骤较繁琐
HashedModuleIdsPlugin 插件
这种方式是对NamedModulesPlugin插件的一种改进,模块的id不再是以相对路径命名,而是根据相对路径生成一个长度小于4的字符串。打包会生成3个文件,manifest,vendor,业务代码,其中manifest主要是记录vendor和业务代码的id和hash值,它的大小只有几kb,小到微不足道。当业务代码变更时,vendor的hash值不会变,变得只有manifest和业务模块的hash,而mainifest中对vendor的id和hash的记录并未更改,从而达到我们的最终目的new webpack.optimize.CommonsChunkPlugin({
names: ['vendor', 'manifest'],
minChunks: Infinity
}),
new webpack.HashedModuleIdsPlugin(),
但是这个插件有个问题:但vendor内容变化是,vendor的hash并未改变,这种情况可能就会在项目更换第三方依赖库的时候导致bug,不过一般开发过程中更换第三方库的情况较少,所以目前没造成什么大的问题