源码 file-loader 哈希生成规则
我们知道,在 Webpack 中有三种哈希:
hash
:一次 compilation 总体的哈希,只要有一个文件修改,整个哈希就会发生变化chunk-hash
:根据chunk
生成的哈希,同一个chunk
中所有文件的哈希相同content-hash
:根据文件内容生成的哈希
在 Vue-cli 默认 Webpack 配置中,对 JS 启用 chunk-hash
,CSS 启用 content-hash
,而图片和字体文件则是 hash
。这样就产生一个问题,修改 JS 代码后,图片和字体的哈希是否会发生变化?
这个问题看起来有点中二,如果修改 JS 代码,导致图片、字体的哈希改变了,显然非常不合理。。但还是抱着好奇的心态去看了源码
file-loader
源码简化之后如下:
// 这里的 loader-utils 是 Webpack 暴露给 loader 的 API
import { getOptions, interpolateName } from 'loader-utils';
export default function loader(content) {
// getOptions 方法用于获取 loader 的配置
const options = getOptions(this);
// 这里的 name 选项是配置中传递的
const name = options.name || '[contenthash].[ext]';
// interpolateName 方法可以根据 name 和 content 内容生成哈希
// 可以保证文件内容没有发生变化的时候,文件名中的 [hash] 字段不变
const url = interpolateName(this, name, { content });
// 拼接文件路径
// 这里的 __webpack_public_path__ 是 Webpack 提供的运行时的全局变量,即 publicPath
let publicPath = `__webpack_public_path__ ${JSON.stringify(url)}`;
// emitFile 是 Loader Context 中的 API,告诉 Webpack 创建一个文件
// 这样 Webpack 就会在 dist 目录下创建一个对应的文件
this.emitFile(url, content);
const esModule =
typeof options.esModule !== 'undefined' ? options.esModule : true;
// 返回一个字符串形式的 JS 模块,显然是在浏览器端执行的
return `${esModule ? 'export default' : 'module.exports ='} ${publicPath};`;
}
// 记得加上这个,默认情况下 Webpack 会把文件内容当做 utf8 字符串处理
// 而图片是二进制的,当做 utf8 会导致图片格式错误
export const raw = true;
上面的代码完全可以正常运行。我们可以看到,file-loader
其实就做了三件事:
- 根据给定的文件名配置和文件内容,生成带有哈希的文件路径;
- 根据生成的文件路径,创建一个文件;
- 最后返回一个字符串形式的 JS 模块,加载这个模块,就可以得到文件路径;
关于 Loader Context,应该有不少小伙伴都知道,例如可以使用 this.callback()
返回多个结果,使用 this.async()
指定异步 loader。这里用到的 this.emitFile()
也是 Loader Context 上的方法,用于创建一个文件。
https://webpack.docschina.org/api/loaders/#thisemitfile
但是可能大家对 loader-utils
了解得比较少,这同样也是 Webpack 暴露给 Loader 的 API,只不过这个是通过第三方库的形式引入的。
这里提一下,
loader-utils
中的getOptions
方法在 v3.2.0 中已经被移除了,Webpack5 可以从 Loader Context 的this.getOptions
方法获取。这相当于是一个破坏性更新,file-loader
中之所以还能使用,是因为依赖版本锁定为"loader-utils": "^2.0.0"
,也就是范围在>=2.0.0 <3.0.0
从上面的代码中可以看出,生成哈希相关逻辑都在 interpolateName
这个方法里面,部分源码如下:
function interpolateName(loaderContext, name, options = {}) {
let filename = name || "[hash].[ext]";
const content = options.content;
// ...
let url = filename;
if (content) {
// Match hash template
url = url
// `hash` and `contenthash` are same in `loader-utils` context
// let's keep `hash` for backward compatibility
.replace(
/\[(?:([^:\]] ):)?(?:hash|contenthash)(?::([a-z] \d*))?(?::(\d ))?\]/gi,
(all, hashType, digestType, maxLength) =>
getHashDigest(content, hashType, digestType, parseInt(maxLength, 10))
);
}
// ...
}
看这边用到了 getHashDigest
方法,我们可以不用关心内部实现。源码中的正则表达式,我们使用 "[hash].[ext]"
进行实验,发现能拿到 hash
或 contenthash
配置信息的,只有第一个参数 all
,其他都是 undefined
。
然而在源码中这个 all
根本就没有传给 getHashDigest
方法,传递的参数中唯一有用的参数就是 content
,也就是文件内容。因此我们可以得出结论,文件名中无论配置 hash
或者 contenthash
,都是等价的,实际上都是 contenthash
,都是根据文件本身的内容生成的,与 Webpack 的构建过程无关。
参考
webpack 源码解析:file-loader 和 url-loader
https://github.com/webpack-contrib/file-loader
https://github.com/webpack/loader-utils
这篇好文章是转载于:学新通技术网
- 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
- 本站站名: 学新通技术网
- 本文地址: /boutique/detail/tanhgfjbge
-
photoshop保存的图片太大微信发不了怎么办
PHP中文网 06-15 -
《学习通》视频自动暂停处理方法
HelloWorld317 07-05 -
Android 11 保存文件到外部存储,并分享文件
Luke 10-12 -
word里面弄一个表格后上面的标题会跑到下面怎么办
PHP中文网 06-20 -
photoshop扩展功能面板显示灰色怎么办
PHP中文网 06-14 -
微信公众号没有声音提示怎么办
PHP中文网 03-31 -
excel下划线不显示怎么办
PHP中文网 06-23 -
excel打印预览压线压字怎么办
PHP中文网 06-22 -
怎样阻止微信小程序自动打开
PHP中文网 06-13 -
TikTok加速器哪个好免费的TK加速器推荐
TK小达人 10-01