hexo-img-onerror 是一个为 Hexo 网站添加备用图片源的插件。
故名思义,它的实现原理是为每一个 img 标签添加一个 onerror 事件,当图片加载失败时,会自动替换为备用图片。
如图所示:
安装与配置
运行以下命令安装插件:
npm install hexo-img-onerror --save
安装插件后,请在 Hexo 项目中配置它。
- 在 Hexo
_config.yml
中添加以下配置,请确保src_prefix
和onerror_src_prefix
一一对应:
img_onerror:
enable: true
src_prefix:
- https://example.com/path1
- https://example.com/path2
onerror_src_prefix:
- https://fallback.com/path1
- https://fallback.com/path2
在你的文章或页面中,添加图像:
![Description](https://example.com/path1/image.jpg)
![Description](https://example.com/path2/image.jpg)
渲染出来的 img
标签会被设置 onerror
事件,重新指定 src
:
<img src="https://example.com/path1/image.jpg" onerror="this.src='https://fallback.com/path1/image.jpg';">
<img src="https://example.com/path2/image.jpg" onerror="this.src='https://fallback.com/path2/image.jpg';">
此时,如果 image.jpg
从 https://example.com/path1/image.jpg 加载失败,浏览器将自动从 https://fallback.com/path1/image.jpg 加载备用图像。
源码解读
function addOnError(data, config) {
// 从 hexo 的 config 中读取 src_prefix 和 onerror_src_prefix 数组
const srcPrefixes = config.src_prefix || [];
const onErrorSrcPrefixes = config.onerror_src_prefix || [];
if (srcPrefixes.length !== onErrorSrcPrefixes.length) {
console.error('src_prefix and onerror_src_prefix must have the same number of elements.');
return data;
}
// 为每一个 img 标签添加 onerror 事件
// 指定图片加载失败时的备用图片链接
return data.replace(/<img [^>]*src="([^"]+)"[^>]*>/g, (match, src) => {
const srcIndex = srcPrefixes.findIndex(prefix => src.startsWith(prefix));
if (srcIndex !== -1) {
const newSrc = src.replace(srcPrefixes[srcIndex], onErrorSrcPrefixes[srcIndex]);
return match.replace(/(<img [^>]*src="[^"]+")/, `$1 onerror="this.onerror=null;this.src='${newSrc}'"`);
}
return match;
});
}
// 注册 hexo 的 filter,在文章渲染后执行 addOnError 函数
hexo.extend.filter.register('after_post_render', (data) => {
const config = hexo.config.img_onerror;
if (config.enable) {
data.content = addOnError(data.content, config);
// 为了防止 post 被多次处理,添加一个标记
data.img_onerror_processed = true;
}
return data;
}, 10);
// 有些主题可能主页也会有 img 标签,所以需要在渲染 html 后再次处理
hexo.extend.filter.register('after_render:html', (html, data) => {
const config = hexo.config.img_onerror;
// 如果插件开启且 post 未被处理过
if (config.enable && !data.img_onerror_processed) {
return addOnError(html, config);
}
return html;
}, 10);
为什么需要它?
我博客的图片放在了家里服务器的 Alist 里,最终的存储是国内的天翼云盘。 从这个服务器获取图片速度很快,但它的缺点是不稳定,有时会因为各种原因导致图片无法访问。
- DDNS 失败了
- 服务器 断网了
- Alist 挂了
- …
此外,这些博客里的图片在 Sharepoint 上也有备份,Sharepoint 的国内访问速度就比较一般了,但胜在稳定。
于是,一个自然的需求就是,为我的网站的所有图片添加一个备用源,让浏览器优先访问 Alist,如果失败了,再访问 Sharepoint。
经过与 AI 的一番交涉,我最终选择了为图片添加 onerror 事件的方式。
还有一种方式是给图片添加 srcset
属性,但是经过我的测试,浏览器并不会优先加载 src
属性,有时候会直接加载 srcset
属性指定的图片,这样就无法实现我想要的效果。