[譯] 原生實現圖片懶加載

在本文中,咱們將研究新的 loading 屬性,它爲 <img><iframe> 帶來了延遲加載的能力。若是你對此感興趣,可查看如下示例:javascript

<img src="celebration.jpg" loading="lazy" alt="..." />
<iframe src="video-player.html" loading="lazy"></iframe>
複製代碼

咱們但願在 ~Chrome 75 中爲 loading 提供支持,而且咱們正在深刻研究即將發佈的新特性。在此以前,讓咱們深刻了解它的工做原理。php

簡介

Web 頁面一般包含大量的圖片,這些圖片將影響網絡流量、頁面尺寸及頁面加載速度。這些圖片中許多處於屏幕外,每每須要用戶滾動頁面才能看到。html

過去,爲了下降屏幕外的圖片對頁面加載時間的影響,開發人員不得不使用 JavaScript 庫(好比:LazySizes)來推遲這些圖片的加載時機,直到用戶將頁面滾動到它們附近。前端

頁面加載 211 張圖片。沒有延遲加載的版本加載了 10 MB 數據。延遲加載版本(使用 LazySizes)僅預先加載了 250 KB 數據 - 其餘圖片將隨着用戶的滾動而加載。查看 WPTjava

若是瀏覽器就能幫你作到避免加載屏幕外的圖片呢?這將有助於加快視窗中內容的加載、減小總體網絡數據量以及低端設備下內存使用量。所以,我很高興地告訴你們,很快就可使用 image 和 iframe 的新屬性 loading 來實現了。android

loading 屬性

loading 屬性容許瀏覽器推遲加載屏幕外的 image 和 iframe 直到用戶將頁面滾動到它們附近。loading 支持三個值:ios

  • lazy:延遲加載。
  • eager:當即加載。
  • auto:由瀏覽器來決定是否延遲加載。

若是不指定該屬性,其默認值爲 auto。git

HTML 標準 正在研究將 <img><iframe>loading 屬性做爲標準的一部分。github

例子

loading 屬性適用於 <img>(包括包含 srcset 屬性及位於 <picture> 內部)和 <iframe>web

<!-- 延遲加載屏幕外的圖片直到用戶滾動到它附近 -->
<img src="unicorn.jpg" loading="lazy" alt=".."/>

<!-- 當即加載(而非延遲加載)圖片 -->
<img src="unicorn.jpg" loading="eager" alt=".."/>

<!-- 瀏覽器決定是否延遲加載圖片 -->
<img src="unicorn.jpg" loading="auto" alt=".."/>

<!-- 延遲加載 <picture> 內的圖片。<img> 用來驅動圖片的加載,所以 <picture> 及 srcset 會將合適的圖片呈如今 <img> 上 -->
<picture>
  <source media="(min-width: 40em)" srcset="big.jpg 1x, big-hd.jpg 2x">
  <source srcset="small.jpg 1x, small-hd.jpg 2x">
  <img src="fallback.jpg" loading="lazy">
</picture>

<!-- 延遲加載設定 srcset 屬性的圖片-->
<img src="small.jpg" srcset="large.jpg 1024w, medium.jpg 640w, small.jpg 320w" sizes="(min-width: 36em) 33.3vw, 100vw" alt="A rad wolf" loading="lazy">

<!-- 延遲加載屏幕外的 iframe 直到用戶滾動到它附近 -->
<iframe src="video-player.html" loading="lazy"></iframe>
複製代碼

由瀏覽器完成「當用戶滾動到附近時」的確切檢測。通常來講,咱們但願瀏覽器在快要進入視窗口以前便開始提取延遲圖片和 iframe 的內容。這將增長圖片或 iframe 在用戶滾動到它們時完成加載的更改。

注意:我曾建議應該將 loading 屬性值做爲屬性名稱,由於它的命名與 decoding 屬性較爲接近。在以前的提議中,相似 lazyload 這樣的屬性沒有被接受,這是由於咱們須要支持多個值(lazyeagerauto)。

特性檢測

咱們已知道爲延遲加載(跨瀏覽器支持)獲取及應用 JavaScript 庫的重要性。loading 的支持狀況能夠經過如下方式進行檢測:

<script> if ('loading' in HTMLImageElement.prototype) { // 瀏覽器支持 `loading`.. } else { // 獲取並應用 polyfill/JavaScript 類庫 // 來替代 lazy-loading。 } </script>
複製代碼

注意:你還可使用 loading 做爲一種漸進的加強功能。支持該屬性的瀏覽器可經過 loading=lazy 得到新的延遲加載能力,不支持該屬性的瀏覽器仍然會加載圖片。

跨瀏覽器的圖片延遲加載

若是跨瀏覽器支持圖片的延遲加載很是重要,那麼僅僅在使用 <img src=unicorn.jpg loading=lazy /> 的標記中進行特性檢測、使用延遲加載庫是不夠的。

該標記需使用相似 <img data-src=unicorn.jpg />(而非 srcsrcset<source>)的屬性,以免在不支持新屬性的瀏覽器下觸發馬上加載。若是瀏覽器支持 loading,可使用 JavaScript 將這些屬性更改成正確的屬性,不然加載類庫。

下面是一個能夠說明其多是什麼樣子的例子。

  • 視窗/一屏展現圖片是常規的 <img> 標籤。data-src 會破壞預加載掃描程序,所以咱們但願避免它出如今視窗中的全部內容中。
  • 咱們在圖片上使用 data-src 以免在不支持的瀏覽器中觸發馬上加載,若是瀏覽器支持 loading,咱們將 data-src 替換爲 src
  • 若是 loading 不被支持,咱們加載一個後備(LazySizes)腳本並啓動它。在這裏,咱們用 class=lazyload 向 LazySizes 指出,哪些圖片要延遲加載。
<!-- 讓咱們在視窗內正常加載這個圖片 -->
<img src="hero.jpg" alt=".."/>

<!-- 讓咱們以延遲加載的方式加載剩餘圖片 -->
<img data-src="unicorn.jpg" loading="lazy" alt=".." class="lazyload"/>
<img data-src="cats.jpg" loading="lazy" alt=".." class="lazyload"/>
<img data-src="dogs.jpg" loading="lazy" alt=".." class="lazyload"/>

<script> (async () => { if ('loading' in HTMLImageElement.prototype) { const images = document.querySelectorAll("img.lazyload"); images.forEach(img => { img.src = img.dataset.src; }); } else { // 動態引入 LazySizes 庫 const lazySizesLib = await import('/lazysizes.min.js'); // 初始化 LazySizes(讀取 data-src & class=lazyload) lazySizes.init(); // lazySizes 在全局環境下工做。 } })(); </script>
複製代碼

示例

看看這個!一個 loading=lazy 示例,展現了整整 100 張小貓圖片

詳見 YouTube 視頻:youtu.be/bhnfL6ODM68

Chrome 實現細節

咱們強烈建議等到 loading 屬性處於穩定版本後再在你的生產環境中使用它。早期測試人員可能會發現如下註解很是有用。

馬上嘗試

轉到 chrome://flags 並同時開啓 "Enable lazy frame loading" 和 "Enable lazy image loading",而後從新啓動 Chrome。

配置

Chrome 延遲加載的實現不只僅基於當前滾動位置的接近程度,還取決於網絡鏈接速度。對於不一樣的網絡鏈接速度,延遲加載 frame 和圖片的視窗距離閾值是硬編碼的,能夠經過命令行覆蓋該值。如下是一個覆蓋圖片延遲加載設置的示例:

canary --user-data-dir="$(mktemp -d)" --enable-features=LazyImageLoading --blink-settings=lazyImageLoadingDistanceThresholdPxUnknown=5000,lazyImageLoadingDistanceThresholdPxOffline=8000,lazyImageLoadingDistanceThresholdPxSlow2G=8000,lazyImageLoadingDistanceThresholdPx2G=6000,lazyImageLoadingDistanceThresholdPx3G=4000,lazyImageLoadingDistanceThresholdPx4G=3000 'https://mathiasbynens.be/demo/img-loading-lazy'
複製代碼

以上命令對應於(當前)默認配置。將全部值更改成 400 以便僅在滾動位置在距離圖片的 400 像素之內開始延遲加載。下面咱們還能夠看到距離閾值設爲 1 像素的另外一個作法(在本文前面的視頻中使用):

canary --user-data-dir="$(mktemp -d)" --enable-features=LazyImageLoading --blink-settings=lazyImageLoadingDistanceThresholdPxUnknown=1,lazyImageLoadingDistanceThresholdPxOffline=1,lazyImageLoadingDistanceThresholdPxSlow2G=1,lazyImageLoadingDistanceThresholdPx2G=1,lazyImageLoadingDistanceThresholdPx3G=1,lazyImageLoadingDistanceThresholdPx4G=1 'https://mathiasbynens.be/demo/img-loading-lazy'
複製代碼

因爲實如今將來幾周內穩定下來,咱們的默認配置極可能會發生變化。

DevTools

Chrome 中 loading 的一個實現細節是它會在頁面加載時獲取前 2 KB 的圖片數據。若是服務器支持範圍請求,則前 2 KB 可能包含圖片尺寸。這使得咱們可以生成/顯示具備相同尺寸的佔位符。若是像是圖標一類的資源的話,前 2 KB 也頗有可能包含整幅圖片了。

Chrome 會在用戶即將看到圖片時抓取其剩餘數據。Chrome DevTools 中要注意的地方是,這可能致使(1)在 DevTools 的網絡面板中「出現」 兩次獲取和(2)爲每一個圖片提供兩個請求的資源定時。

服務端肯定 loading 支持

在一個美好的世界中,你不須要依賴客戶端上的 JavaScript 特性檢測來決定是否須要加載兼容庫 — 你須要在提供包含 JavaScript 延遲加載庫的 HTML 以前處理此問題。客戶端提示能夠啓用此類檢查。

傳遞 loading 參數的提示已經被考慮,但目前正處於早期討論階段。

總結

試試看 <img loading>,並讓咱們知道你的想法。我對你們如何探索跨瀏覽器的經驗,及是否有任何咱們錯過的邊緣狀況特別感興趣。

參考資料

感謝 Simon Pieters、Yoav Weiss 和 Mathias Bynens 的反饋。很是感謝 Ben Greenstein、Scott Little、Raj T 和 Houssein Djirdeh 在 LazyLoad 上的工做。

若是發現譯文存在錯誤或其餘須要改進的地方,歡迎到 掘金翻譯計劃 對譯文進行修改並 PR,也可得到相應獎勵積分。文章開頭的 本文永久連接 即爲本文在 GitHub 上的 MarkDown 連接。


掘金翻譯計劃 是一個翻譯優質互聯網技術文章的社區,文章來源爲 掘金 上的英文分享文章。內容覆蓋 AndroidiOS前端後端區塊鏈產品設計人工智能等領域,想要查看更多優質譯文請持續關注 掘金翻譯計劃官方微博知乎專欄

相關文章
相關標籤/搜索