關於圖片懶載入的幾種方案

  • 2019 年 12 月 20 日
  • 筆記

懶載入,顧名思義,在當前網頁,滑動頁面到能看到圖片的時候再載入圖片

故問題拆分成兩個:

  1. 如何判斷圖片出現在了當前視口 (即如何判斷我們能夠看到圖片)
  2. 如何控制圖片的載入

方案一

如何判斷圖片出現在了當前視口

clientTopoffsetTopclientHeight 以及 scrollTop 都是一些元素高度的屬性,這些高度都代表了什麼意思呢?

這我以前有可能是知道的,那時候我比較單純,喜歡死磕。我現在想通了,背不過的東西就不要背了,如果你想了解,可以看下這個:https://segmentfault.com/a/1190000019507352?utm_source=tag-newest

所以它有一個問題:複雜瑣碎不好理解!

僅僅知道它靜態的高度還不夠,我們還需要知道動態的高度

如何動態?監聽 window.scroll 事件

如何控制圖片的載入

<img data-src="shanyue.jpg">

首先設置一個臨時屬性 data-src,載入時再使用 src 代替 data-src

方案二

改進一下

如何判斷圖片出現在了當前視口

引入一個新的 API, Element.getBoundingClientRect() 方法返回元素的大小及其相對於視口的位置。

那如何判斷圖片出現在了當前視口呢,根據示例圖示意,程式碼如下,這個就比較好理解了,就可以很容易地背會(就可以愉快地去面試了)。

// clientHeight 代表當前視口的高度  img.getBoundingClientRect().top < document.documentElement.clientHeight

監聽 window.scroll 事件也優化一下

加個節流器,提高性能。工作中一般使用 lodash.throttle 就可以了,萬能的 lodash 啊!

_.throttle(func, [wait=0], [options={}])

參考 什麼是防抖和節流,他們的應用場景有哪些

方案三

再改進一下

如何判斷圖片出現在了當前視口

方案二使用的方法是: window.scroll 監聽 Element.getBoundingClientRect() 並使用 _.throttle 節流

一系列組合動作太複雜了,於是瀏覽器出了一個三合一事件: IntersectionObserver API,一個能夠監聽元素是否到了當前視口的事件,一步到位!

事件回調的參數是 IntersectionObserverEntry 的集合,代表關於是否在可見視口的一系列值

其中,entry.isIntersecting 代表目標元素可見

const observer = new IntersectionObserver((changes) => {    // changes: 目標元素集合    changes.forEach((change) => {      // intersectionRatio      if (change.isIntersecting) {        const img = change.target        img.src = img.dataset.src        observer.unobserve(img)      }    })  })    observer.observe(img)

當然,IntersectionObserver 除了給圖片做懶載入外,還可以對單頁應用資源做預載入。

如在 next.js v9 中,會對視口內的資源做預載入,可以參考 next 9 production optimizations

<Link href="/about">    <a>關於山月</a>  </Link>

方案四

瀏覽器覺得懶載入這事可以交給自己做,你們開發者加個屬性就好了。實在是…!

<img src="shanyue.jpg" loading="lazy">

不過目前瀏覽器兼容性不太好,看下圖:

幾乎可以說目前是Chrome特有的特性了.關於 loading 屬性的文章也可以查看 Native image lazy-loading for the web!

總結

  • window.scroll 監聽各種 topheight 並使用 _.throttle 節流,但是不好理解各種 tophegith
  • window.scroll 監聽 getBoundingClientRect 並使用 _.throttle 節流,沒有一個統一事件,相對複雜
  • IntersectionObserver,瀏覽器推出了一個事件,方便簡單
  • img.loading=lazy,瀏覽器直接給你解決,開發者直接標註屬性