hexo-img-onerror 是一个为 Hexo 网站添加备用图片源的插件。License

故名思义,它的实现原理是为每一个 img 标签添加一个 onerror 事件,当图片加载失败时,会自动替换为备用图片。

如图所示:

安装与配置

运行以下命令安装插件:

npm install hexo-img-onerror --save

安装插件后,请在 Hexo 项目中配置它。

  1. 在 Hexo _config.yml 中添加以下配置,请确保 src_prefixonerror_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.jpghttps://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 属性指定的图片,这样就无法实现我想要的效果。