關於圖片懶載入的幾種方案
- 2019 年 12 月 20 日
- 筆記
懶載入,顧名思義,在當前網頁,滑動頁面到能看到圖片的時候再載入圖片
故問題拆分成兩個:
- 如何判斷圖片出現在了當前視口 (即如何判斷我們能夠看到圖片)
- 如何控制圖片的載入
方案一
如何判斷圖片出現在了當前視口
clientTop
,offsetTop
,clientHeight
以及 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
監聽各種top
與height
並使用_.throttle
節流,但是不好理解各種top
與hegith
window.scroll
監聽getBoundingClientRect
並使用_.throttle
節流,沒有一個統一事件,相對複雜IntersectionObserver
,瀏覽器推出了一個事件,方便簡單img.loading=lazy
,瀏覽器直接給你解決,開發者直接標註屬性