前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >图片错误自动重载

图片错误自动重载

作者头像
神仙朱
发布2021-07-20 16:52:45
1.3K0
发布2021-07-20 16:52:45
举报

平常在我们的项目中,必然会引用很多图片。

但是我们通常只管给图片赋值一个链接

爱怎么加载怎么加载,失败我也不管

这其实对于一个应用来说是非常不完善的

因为每个用户的网络情况无法预估(比如地铁上),图片加载失败必然导致页面就无法浏览或者体验差

这肯定不是一个好应用

比如我在地铁上打开施肥,网络不行,图片全部加载失败了,直接返回又重新进来,很烦躁的啊

所以通常我们会给图片加上一个 失败重载的机制。

那么今天我们就来看一下怎么实现这个 图片失败重载的机制。

1

简单描述

先简单说一下基本的处理

每一个 img 在加载失败后重新加载最多3次,超过3次就 换上默认图片

因为我们在引入我们js 或者 直出的时候,页面已经有 img 元素在加载了所以我们需要对已经存在的 img 进行处理

所以处理分两条路

1、处理已经存在的 img

2、监听动态插入的 img

来看一下流程图

那么下来我们来分别看下这两个处理流程

2

处理现存图片

我们需要获取到所有现有的图片,然后逐个遍历去判断图片是否加载失败

但是对于可能已经加载完成的图片我们怎么判断是否加载失败呢?

img 元素如果加载完成,会有一个属性 complete = true

但是我们还不知道这个图片是成功还是失败

我们需要从另一个属性去判断

那就是 naturalWidth,这个表示图片的原始宽度,如果该值为0,那么就表示这个图片加载失败啦

条件就是

3

动态监听新 img错误

我们是不是 监听 img 元素的插入,然后给 img 元素加上一个 onerror 事件?

当然不是啦

我们可以在全局监听一个 error 事件,并且在 事件回调中 判断元素是 img 才进行处理

那么具体是怎么做呢

代码语言:javascript
复制
document.body.addEventListener(

第三个参数useCapture必须设置为 true,表示事件采取事件捕获原则。默认是true,采取事件冒泡原则

因为img或者script标签发生error时不会向上冒泡,所以父级以上元素监听error则不会被触发。不过既然不会冒泡,我们只能使用捕获保证先执行父级元素事件

4

处理图片错误

好了,上面说完了两条处理分支,现在来说一下共同的错误处理分支

我们的原则是

1、不处理懒加载图片

2、图片加载未超过3次,重载图片,超过3次使用默认图片

1不处理懒加载图片

首先懒加载的图片在没有划上屏幕的时候,是没有加载的,src为空,只在data-src或者lazy-src保存原图片链接

所以这些图片不适用于错误重载,直接跳过

2图片加载未超3次则重载

我们要怎么知道图片重载了几次?

做法是,只要重载之后,在图片链接后面拼上一个参数 reloadcounts = 1继续重载,参数值就获取+1

如果次数超过3,那么就使用默认图片

具体代码如下

代码语言:javascript
复制
// 给图片 url 加上 重载次数参数

5

代码实现

接下来看下简单的代码实现

代码语言:javascript
复制
// 处理已经存在的图片
function observeStatic(target) {
  getImgElements(target, (nodes) => {    nodes.forEach((node) => {      
        const lazySrc =
          node.getAttribute('data-src') || node.getAttribute('lazy-src');      
        if (!lazySrc) {
          wrapImg(node);
        }
    });
  });}


// 监听动态插入的 img
function observeDynamic(target) {
  target.addEventListener(    
    'error',
    errorHandler,    
    true // 只有捕获阶段能捕捉到img加载error
  );  


  const errorHandler = (e) => {    
      const element = e.target;
   
      if (element instanceof HTMLImageElement) {      
          const src = element.getAttribute('src');    
       // lazyload图片不处理    
        const lazySrc =   
          element.getAttribute('data-src') || 
          element.getAttribute('lazy-src');      
          if (!src || lazySrc) {        
            return;
          }


          setTimeout(() => {        
            // 延迟到onError执行后处理
            ImgErrorHandler(element);
          }, 0);
    }
  };}


// 图片错误处理函数
const ImgErrorHandler = (element) => {  
  const src = element.src;  
  const isOrigin = !element[originalSrcKey];  


  // 图片还没有 重载过 || 没有超过重载次数
  if (isOrigin || !ifImgLoadOverMaxCount(src)) {    
    // 如果重来没有加载,那么就保存元数据
    if (isOrigin) {
      element[originalSrcKey] = src;
    }    element.src = addReloadFlag(src);    
    return;
  }  


  // defaultUrl也加载失败,没救了
  element.onerror = null;
  element.src = DEFAULT_IMG;};


// 获取到元素下所有的 img 标签
function getImgElements(target, callback) {  
   const nodes = target.querySelectorAll('img');  
   if (typeof callback === 'function') {
     callback([].slice.call(nodes));
   }}


// 处理 img 标签
function wrapImg(element) {  


  if (wrappedImgElements.indexOf(element) !== -1) {    
     return;
  }


  wrappedImgElements.push(element);  


  const originalOnError = element.onerror;
  element.onerror = function (e) {    
    if (typeof originalOnError === 'function') {
      originalOnError.call(this, e);
    }

    ImgErrorHandler(element);  };  


  if (element.complete && element.naturalWidth === 0) {    
    // 意味着没加载成功,直接重试
    ImgErrorHandler(element);
  }}


let DYNAMIC_TARGET = null;


function obseverImg(target) {  
  if (DYNAMIC_TARGET) {    
    // 不允许多次observe
    return;
  }
  DYNAMIC_TARGET = target;
  observeStatic(target);
  observeDynamic(target);
}

然后使用的时候,如果你是监听全局,可以像这样

代码语言:javascript
复制
obseverImg(docuemnt.body)

但是有时不会是这样,因为不会全局使用一张默认图片的

所以这里支持传入 目标dom 元素,只处理部分 img

4

总结

通常在我们的应用中,一个完善的流程不止是

失败-》重载*3-》失败-》默认

在中间可能还要加上一个代理的流程,比如可能图片访问不了,那么我们加个代理可能就好了

或者中间还有更多的判断,比如符合某个规则的图片才有重载机制 等等

反正我们的目的就是增加图片成功的概率,但是本文只是记录一下基本原理,所以复杂更完善的流程就省略了

最后

鉴于本人能力有限,难免会有疏漏错误的地方,请大家多多包涵, 如果有任何描述不当的地方,欢迎后台联系本人

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-07-12,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 神仙朱 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档