Vue 和 jQuery 的圖片容錯處理方案

網(wǎng)頁在獲取圖片資源的時候,經(jīng)常會由于 資源路徑無效、網(wǎng)絡(luò)環(huán)境不理想、服務(wù)器過載 等原因?qū)е沦Y源加載超時或失敗。為了保證良好的用戶體驗,我們需要對圖片加載做容錯處理。

最簡單的方式就是通過綁定 img 元素的 error 事件,在圖片加載失敗時顯示備用圖片 error.jpg

若采用此方式,需要在 error 事件觸發(fā)時取消事件的綁定,避免當(dāng) error.jpg 也加載失敗時死循環(huán)拉取資源。

<img src="image-path.jpg" onerror="this.onerror=null;this.src='error.jpg'">

如果需要對 背景圖片background-image) 實施容錯處理呢?

如果需要在圖片加載過程中加 loading 效果呢?

這個時候就需要 “替身” 發(fā)揮作用了 ——

我們可以利用一個對用戶不可見的 img 元素,來驗證圖片資源的合法性。

jQuery 環(huán)境

額外創(chuàng)建一個不插入 DOM 節(jié)點樹的 img 元素 “替身”,src 屬性值為目標(biāo)資源的 url。若 “替身” 能成功獲取目標(biāo)資源(load 事件觸發(fā)),即讓目標(biāo)節(jié)點加載該資源,否則(error 事件觸發(fā))加載備用資源。

注意,重復(fù)加載同一個資源時并不會產(chǎn)生額外的網(wǎng)絡(luò)消耗,瀏覽器會從本地緩存獲取該資源。

在 jQuery 環(huán)境下,建議以插件的形式擴展,維持鏈?zhǔn)秸{(diào)用的特性:

$.fn.img = function(opts) {
  const $img = document.createElement('img');
  // 未加載完成時,顯示 loading
  this.css('background-image', `url('${opts.loading}')`);
  $img.onload = () => {
    this.css('background-image', `url('${opts.src}')`);
  };
  $img.onerror = () => {
    this.css('background-image', `url('${opts.error}')`);
  };
  $img.src = opts.src;
  return this;
}

$('.image').img({
  src: 'success.jpg',
  error: 'error.jpg',
  loading: 'loading.gif',
});

預(yù)覽地址:https://codepen.io/JunreyCen/pen/WBapBw

jQuery + 模板引擎

如果使用了模板引擎(譬如 lodash_.template 函數(shù))的渲染方式,且希望維持 數(shù)據(jù)驅(qū)動視圖 的模式時,可以參考下面的處理:

<div id="app"></div>
<template id="tpl">
  <div class="image" style="background-image: url('<%= loading %>')"></div>
  <img 
    style="display: none"
    src="<%= src %>"
    onload="$(this).siblings('.image').css('background-image', 'url(<%= src %>)')"
    onerror="$(this).siblings('.image').css('background-image', 'url(<%= error %>)')">
</template>

<script>
$(function() {
  $('#app').append(_.template($('#tpl').html())({
    src: 'success.jpg',
    error: 'error.jpg',
    loading: 'loading.gif',
  }));
});
</script>

題外話,推薦把 demo 中的處理封裝成模板組件,配合 CSS 命名空間 就可以實現(xiàn)組件化了。鑒于 jQuery + 模板引擎 這類技術(shù)棧流行度已經(jīng)沒那么高了,這里不再單獨提供例程。

Vue 環(huán)境

我們通過綁定 vue 指令 來實現(xiàn)圖片的容錯處理。其好處在于,每當(dāng)圖片資源的 src 被初始化或更新時,vue 指令都可以捕捉到變化,并容錯處理后再響應(yīng)式地作用于目標(biāo)節(jié)點。

vue 官方文檔 中對于 自定義指令 有詳細(xì)的教程,這里就不多說了,直接貼代碼。

<div id="app"></div>
<template id="tpl">
  <div class="image" v-img="{
    src: 'success.jpg',
    error: 'error.jpg',
    loading: 'loading.gif',
  }"></div>
</template>

<script>
  function imgHandler($el, binding) {
    const opts = binding.value;
    const $img = document.createElement('img');
    $el.style.backgroundImage = `url('${opts.loading}')`;
    $img.onload = () => {
      $el.style.backgroundImage = `url('${opts.src}')`;
    };
    $img.onerror = () => {
      $el.style.backgroundImage = `url('${opts.error}')`;
    };
    $img.src = opts.src;
  }

  Vue.directive('img', {
    inserted: imgHandler,
    update: imgHandler,
  });

  new Vue({
    el: '#app',
    template: '#tpl',
  });
</script>

踩坑點

基于上面提供的 vue demo,我們來模擬一個場景:

  • 目標(biāo)節(jié)點的 src 被更新;
    譬如,src 先被賦值為 path-to-image-A.jpg,再更新為 path-to-image-B.jpg。
  • 圖片資源的加載速度不理想;

此時,萬一資源 A 的加載速度比資源 B 還要慢,就會出現(xiàn)歷史資源(A)把最新資源(B)覆蓋掉的問題。我們稍微修改下 demo 來實現(xiàn)這個場景:

<div class="image" v-img="{
  // ...
  src,
  delay,
}"></div>

<script>
  function imgHandler($el, binding) {
    // ...
    if (opts.delay) {
      // 模擬圖片加載延遲
      setTimeout(() => {
        $img.src = opts.src;
      }, opts.delay);
    } else {
      $img.src = opts.src;
    } 
  }

  new Vue({
    data() {
      return {
        src: '',
        delay: 0,
      };
    },
    mounted() {
      this.delay = 200;
      this.src = 'success.jpg';
      this.$nextTick(() => {
        this.delay = 0;
        this.src = 'success_2.jpg';
      });
    },
  });
</script>

或者直接看 demo 效果:https://codepen.io/JunreyCen/pen/JqmNQP

優(yōu)化方案

解決方案也很簡單,我們只需要把所有 “替身” 都記錄下來,在更新時把上一次創(chuàng)建的 “替身” 清理掉。即使出現(xiàn)歷史資源的捕獲時機比最新資源的還要靠后的情況,由于歷史資源的 onloadonerror 方法已經(jīng)被重置,不會產(chǎn)生影響。

最終方案例程:https://github.com/JunreyCen/blog-demo/blob/master/image-handler/vue.2.html

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 一:什么是閉包?閉包的用處? (1)閉包就是能夠讀取其他函數(shù)內(nèi)部變量的函數(shù)。在本質(zhì)上,閉包就 是將函數(shù)內(nèi)部和函數(shù)外...
    xuguibin閱讀 10,019評論 1 52
  • 第一部分 HTML&CSS整理答案 1. 什么是HTML5? 答:HTML5是最新的HTML標(biāo)準(zhǔn)。 注意:講述HT...
    kismetajun閱讀 28,797評論 1 45
  • 前端開發(fā)面試題 面試題目: 根據(jù)你的等級和職位的變化,入門級到專家級,廣度和深度都會有所增加。 題目類型: 理論知...
    怡寶丶閱讀 2,678評論 0 7
  • $HTML, HTTP,web綜合問題 1、前端需要注意哪些SEO 2、 的title和alt有什么區(qū)別 3、HT...
    Hebborn_hb閱讀 4,771評論 0 20
  • 第一篇地址:《如何閱讀一本書》摘抄(第一篇) 第二篇地址:《如何閱讀一本書》摘抄(第二篇) 第三篇 閱讀不同讀物的...
    115c3253903d閱讀 2,617評論 0 3

友情鏈接更多精彩內(nèi)容