Vue 和 jQuery 的圖片容錯(cuò)處理方案

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

最簡(jiǎn)單的方式就是通過(guò)綁定 img 元素的 error 事件结洼,在圖片加載失敗時(shí)顯示備用圖片 error.jpg黎做。

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

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

如果需要對(duì) 背景圖片background-image) 實(shí)施容錯(cuò)處理呢筷厘?

如果需要在圖片加載過(guò)程中加 loading 效果呢鸣峭?

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

我們可以利用一個(gè)對(duì)用戶(hù)不可見(jiàn)的 img 元素,來(lái)驗(yàn)證圖片資源的合法性酥艳。

jQuery 環(huán)境

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

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

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

$.fn.img = function(opts) {
  const $img = document.createElement('img');
  // 未加載完成時(shí),顯示 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ū)動(dòng)視圖 的模式時(shí)陵叽,可以參考下面的處理:

<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>

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

Vue 環(huán)境

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

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

<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>

踩坑點(diǎn)

基于上面提供的 vue demo栓撞,我們來(lái)模擬一個(gè)場(chǎng)景:

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

此時(shí),萬(wàn)一資源 A 的加載速度比資源 B 還要慢挽懦,就會(huì)出現(xiàn)歷史資源(A)把最新資源(B)覆蓋掉的問(wèn)題翰意。我們稍微修改下 demo 來(lái)實(shí)現(xiàn)這個(gè)場(chǎng)景:

<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)化方案

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

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市形病,隨后出現(xiàn)的幾起案子客年,更是在濱河造成了極大的恐慌,老刑警劉巖漠吻,帶你破解...
    沈念sama閱讀 218,941評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件量瓜,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡侥猩,警方通過(guò)查閱死者的電腦和手機(jī)榔至,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)欺劳,“玉大人唧取,你說(shuō)我怎么就攤上這事』幔” “怎么了枫弟?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,345評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)鹏往。 經(jīng)常有香客問(wèn)我淡诗,道長(zhǎng),這世上最難降的妖魔是什么伊履? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,851評(píng)論 1 295
  • 正文 為了忘掉前任韩容,我火速辦了婚禮,結(jié)果婚禮上唐瀑,老公的妹妹穿的比我還像新娘群凶。我一直安慰自己,他們只是感情好哄辣,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,868評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布请梢。 她就那樣靜靜地躺著赠尾,像睡著了一般。 火紅的嫁衣襯著肌膚如雪毅弧。 梳的紋絲不亂的頭發(fā)上气嫁,一...
    開(kāi)封第一講書(shū)人閱讀 51,688評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音够坐,去河邊找鬼寸宵。 笑死,一個(gè)胖子當(dāng)著我的面吹牛咆霜,可吹牛的內(nèi)容都是我干的邓馒。 我是一名探鬼主播嘶朱,決...
    沈念sama閱讀 40,414評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼蛾坯,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了疏遏?” 一聲冷哼從身側(cè)響起脉课,我...
    開(kāi)封第一講書(shū)人閱讀 39,319評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎财异,沒(méi)想到半個(gè)月后倘零,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,775評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡戳寸,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年呈驶,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片疫鹊。...
    茶點(diǎn)故事閱讀 40,096評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡袖瞻,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出拆吆,到底是詐尸還是另有隱情聋迎,我是刑警寧澤,帶...
    沈念sama閱讀 35,789評(píng)論 5 346
  • 正文 年R本政府宣布枣耀,位于F島的核電站霉晕,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏捞奕。R本人自食惡果不足惜牺堰,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,437評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望颅围。 院中可真熱鬧伟葫,春花似錦、人聲如沸谷浅。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,993評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至撼玄,卻和暖如春夺姑,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背掌猛。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,107評(píng)論 1 271
  • 我被黑心中介騙來(lái)泰國(guó)打工盏浙, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人荔茬。 一個(gè)月前我還...
    沈念sama閱讀 48,308評(píng)論 3 372
  • 正文 我出身青樓废膘,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親慕蔚。 傳聞我的和親對(duì)象是個(gè)殘疾皇子丐黄,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,037評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • 一:什么是閉包?閉包的用處孔飒? (1)閉包就是能夠讀取其他函數(shù)內(nèi)部變量的函數(shù)灌闺。在本質(zhì)上,閉包就 是將函數(shù)內(nèi)部和函數(shù)外...
    xuguibin閱讀 9,615評(píng)論 1 52
  • 第一部分 HTML&CSS整理答案 1. 什么是HTML5坏瞄? 答:HTML5是最新的HTML標(biāo)準(zhǔn)桂对。 注意:講述HT...
    kismetajun閱讀 27,486評(píng)論 1 45
  • 前端開(kāi)發(fā)面試題 面試題目: 根據(jù)你的等級(jí)和職位的變化,入門(mén)級(jí)到專(zhuān)家級(jí)鸠匀,廣度和深度都會(huì)有所增加蕉斜。 題目類(lèi)型: 理論知...
    怡寶丶閱讀 2,587評(píng)論 0 7
  • $HTML, HTTP缀棍,web綜合問(wèn)題 1宅此、前端需要注意哪些SEO 2、 的title和alt有什么區(qū)別 3睦柴、HT...
    Hebborn_hb閱讀 4,600評(píng)論 0 20
  • 第一篇地址:《如何閱讀一本書(shū)》摘抄(第一篇) 第二篇地址:《如何閱讀一本書(shū)》摘抄(第二篇) 第三篇 閱讀不同讀物的...
    115c3253903d閱讀 2,471評(píng)論 0 3