前言
前篇文章介绍了 Webpack 里的三种 hash 类型,如何给构建输出的文件加 hash 值:juejin.cn/post/728000...
日常开发中,可能会遇到有些第三方库不是通过 npm 引用,而是直接通过 download 下来的 js\css 文件,然后直接通过 append 到 html head 里全局使用的。这样就会有两个操作需要支持:
- 把用到的 js\css 静态文件 copy 到服务器环境路径下,即 Webpack
output.path
路径下。 - 把这些 js\css 静态文件 append 到 html head中,并解决 version 更新缓存问题。
copy-webpack-plugin
可以将需要在html里直接引用的第三方js\css文件,copy 到 output 下。用法很简单。
js
const CopyPlugin = require("copy-webpack-plugin");
plugins: [
new CopyPlugin({
patterns: [
{ context: './lib', from: '**', to: 'lib', info: { minimized: true } },
],
}),
此例是把 ./lib
路径下所有文件 copy 到 Webpack 配置的 output.path
路径下的 ib
路径下。
也可以支持单个文件 copy,也支持glob语法匹配,更多用法参考官网。
html-webpack-tags-plugin
配合 html-webpack-plugin
,支持把具体的 js\css 静态文件引入到 html 中。
js
const HtmlWebpackTagsPlugin = require('html-webpack-tags-plugin');
plugins: [
new HtmlWebpackPlugin(),
new HtmlWebpackTagsPlugin({
links: [
`lib/lib1.css`,
],
scripts: [
`lib/lib1.js`,
],
append: false, // 设置为false会把引入的文件顺序高于html-webpack-plugin中的chunk文件
hash: true, // 引入文件时,加上hash后缀
}),
这里注意 hash: true
这个属性,会给文件自动加上hash后缀。相当于给这些文件加上了version,就支持了缓存。
生成的 html 文件中可以看到,lib文件后面自动加上了hash参数。
如果文件名本来就有参数,也可以单独设置某个文件的hash。
js
scripts: [
{
path: `lib/lib2.js?p1=123`,
hash: (path, hash) => path + '&hash=' + hash, // 只作用单个文件,生成 lib/lib2.js?p1=123&hash={hash}
},
`lib/lib1.js`,
],
hash: true, // 作用所有文件
把hash加到文件名中
Webpack 构建 chunk 文件,都支持文件名内部加 hash,比如这样 index.{hash}.js
,html-webpack-tags-plugin
也可以支持。
js
new CopyPlugin({
patterns: [
// 首先copy文件时,给文件名加上hash
{ context: './lib', from: '**', to: 'lib/[name].[hash][ext]', info: { minimized: true } },
],
}),
new HtmlWebpackTagsPlugin({
links: [
`lib/lib1.css`,
],
scripts: [
`lib/lib1.js`,
],
append: false,
hash: function (assetName, hash) {
// 可以自定义append时的文件名吗,修改原文件名加上hash
assetName = assetName.replace(/\.js$/, '.' + hash + '.js');
assetName = assetName.replace(/\.css$/, '.' + hash + '.css');
return assetName;
},
}),
这样就跟 chunk 文件名一致了。
contenthash
但是还有个问题,Webpack 的 hash 有三种类型,hash
chunkhash
contenthash
,这里的hash对应的是第一种 hash
类型,即对应全局唯一hash值,任何入口文件改动时,都会更改这个hash,这样就有两个问题。
- 第三方文件没改动,但是改了其它code,则构建时会改变这个hash值,浏览器加载第三方文件时,就会重新请求,不会走缓存。
- 如果只改动第三方文件,不改其它code,那构建时这个hash值还是旧的,这样浏览器就还会读缓存。
对于 Webpack 三种 hash 类型区别,参考之前文章:怎么给 Webpack 构建生成的文件加version?
那能不能用 contenthash
呢?每次构建时,通过 html-webpack-tags-plugin
引用的文件都会根据自己的文件内容生成 hash,这样上面俩问题就解决了。
不幸的是 html-webpack-tags-plugin
不支持 contenthash,得自己写逻辑生成:github.com/jharris4/ht...
实现也不复杂,原理就是在调用 plugins 之前,通过 crypto 或 md5 之类的工具,把这些文件挨个转换生成一个 contenthash,然后在 copy plugin 和 tags plugin 里把 contenthash 拼接到对应文件名里。
不用hash
不过有个更简单的方案,把第三方文件放在以version命名的路径下,或者直接给文件名里加上version。例如:lib/v1.1/lib1.js
或 lib/lib1-1.1.js
,每次更新版本都改下folder或file name,也就不用加hash了。
这样有个缺点是每次更新第三方,都得改下文件名以及 Webpack 里配置的文件名。
运行时获取hash
有些静态 js\css 是需要在js里运行时引用的,并不是直接 append 到 html 中的,这种是需要在运行时获取 hash 值,Webpack 提供了一个编译变量 __webpack_hash__
。
webpack.docschina.org/api/module-...
这个变量同样是全局的 hash 值。
js
const src = '/lib/lib1.js?' + __webpack_hash__;
const tag = document.createElement('script');
tag.type = 'text/javascript';
tag.src = src;
document.head.appendChild(tag);
编译后的代码里,可以debug看下取到了hash值。
总结
本文主要介绍了如何通过 Webpack,每次构建时把一些例如第三方的 js\css 静态文件 copy 到 output 中,并且自动引用到 html head 中,而且支持加 hash 支持 version 缓存。主要用到了三个 Webpack plugins:
- copy-webpack-plugin:github.com/webpack-con...
- html-webpack-plugin:github.com/jantimon/ht...
- html-webpack-tags-plugin:github.com/jharris4/ht...