圖片懶加載原理 - 從簡單到複雜


做者:hateonionjavascript

https://hateonion.me/posts/19jan30/php

圖片懶加載是一個很重要的前端性能優化手段。這篇文章將從懶加載的最簡單場景開始介紹,逐步增長複雜度,但願能講清楚常見的圖片懶加載場景及在該場景下對應的解決辦法,也但願對你有所幫助。

爲何要作圖片的懶加載

假設在用戶訪問某個頁面時就加載這個頁面所有的圖片(即便這些圖片並不處在用戶的當前的視窗中),在弱網環境或者網速較慢的環境下,這些「冗餘」圖片的下載會佔用用戶原本就很是有限的帶寬,傷害用戶體驗(好比影響其餘資源的下載)。因此對於網站的圖片,理想的作法是懶加載(按需加載)。

圖片懶加載的原理

在瀏覽器內部對於各類資源有着一套本身的優先級定義,瀏覽器會優先加載優先級高的資源。
若是咱們不去進行圖片的懶加載,默認狀況下,資源的priority以下。
這些優先級標記爲high的圖片會佔用其餘資源的下載帶寬,可能會形成某些比較關鍵的資源(好比xhr call)加載緩慢,拖慢頁面速度。

圖片懶加載的簡單實現

圖片懶加載的思路通常時當頁面加載時加載一個尺寸很小的佔位圖片(1kb如下),而後再經過js選擇性的去加載真正的圖片。
一個最簡單的的實現以下:
<!-- index.html --> <img src="placeholder.jpg" src="real_image.jpt" />
   
   
    
    
             
    
    
// index.css
img[src] { filter: blur(0.2em); }
img { filter: blur(0em); transition: filter 0.5s; }
   
   
    
    
             
    
    
(function lazyLoad(){ const imageToLazy = document.querySelectorAll('img[src]'); const loadImage = function (image) { image.setAttribute('src', image.getAttribute('src')); image.addEventListener('load', function() { image.removeAttribute("src"); }) }
imageToLazy.forEach(function(image){ loadImage(image); })})()
經過懶加載以後,資源優先級以下。

圖片懶加載的進階實現–滾動加載

上面的方案並不完美,對於用戶來講,不在視窗中的圖片可能根本不是用戶當前關心的圖片,因此咱們可讓這些圖片出如今用戶視窗中再進行加載。
運用Intersection Observer 咱們能夠作到當圖片滾動到視窗後再加載該圖片。
   
   
    
    
             
    
    
(function lazyLoad(){ const imageToLazy = document.querySelectorAll('img[src]'); const loadImage = function (image) { image.setAttribute('src', image.getAttribute('src')); image.addEventListener('load', function() { image.removeAttribute("src"); }) }

const intersectionObserver = new IntersectionObserver(function(items, observer) { items.forEach(function(item) { if(item.isIntersecting) { loadImage(item.target); observer.unobserve(item.target); } }); });
imageToLazy.forEach(function(image){ intersectionObserver.observe(image); })})()
上面的這些demo都在https://github.com/hateonion/lazy-load 這個repo裏面。

如何選擇合適的Placeholder圖片

在上面的demo中咱們使用了placeholder圖片,實際上,圖片所佔的位置是否肯定對於咱們選擇placeholder圖片有着很大的影響。

圖片尺寸已知

圖片尺寸已知出現的場景通常是博文的題圖或者網站中一些固定尺寸的thumbnail,這些圖的尺寸通常固定且通常不會發生改變。對於這種場景,咱們能夠加載對應尺寸的placeholder圖片(如上一節的demo)。咱們能夠本身裁剪對應尺寸的的placeholder圖片或者使用相似http://placeholder.com/ 這樣的服務來獲取placeholder圖片。

圖片尺寸未知

圖片尺寸未知的狀況下通常咱們須要生成對應的thumbnail而後去加載咱們生成的thumbnail去作placeholder。爲了生成這些thumbnail你能夠調用imagemagick或者調用一些在線的圖片分割服務(好比七牛)

懶加載防止佈局抖動

在圖片懶加載時,因爲圖片的尺寸不定,瀏覽器難以計算須要給圖片預留出的位置。因此當圖片加載完成後會出現網頁佈局的抖動。
(image from From http://davidecalignano.it/lazy-loading-with-responsive-images-and-unknown-height/)
即便咱們選擇的placeholder很小,能夠在毫秒級別完成下載,用戶可能意識不到佈局的抖動。可是在一些性能比較差的設備上,這種佈局的抖動仍是會必定程度上影響用戶的體驗。爲了徹底避免佈局閃動,咱們能夠採用aspect ratio boxes 的技術來製做一個佔位用的元素。
   
   
    
    
             
    
    
<div class="lazy-load__container feature"> <img src="placeholder.jpg" src="real.jpg" /></div>
   
   
    
    
             
    
    
.lazy-load__container{ position: relative; display: block; height: 0;}
.lazy-load__container.feature { // feature image 的高寬比設置成42.8% // 對於其餘圖片 好比 post圖片,高寬比可能會不一樣,可使用其餘css class去設置 padding-bottom: 42.8%;}
.lazy-load__container img { position: absolute; top:0; left:0; height: 100%; width: 100%;}
結果
上面這個實現的原理其實很簡單,因爲 padding-bottom (或者 padding-top)聲明爲百分比時是根據元素生成的box的 width 去計算百分比的,因此咱們經過padding-bottom去聲明一個對應高寬比的container。而這個container的具體尺寸會由尺寸肯定的外層元素肯定,可是高寬比始終保持一致。
而圖片的尺寸設置成100%container的尺寸保證圖片始終和container的尺寸保持一致。
須要注意的是上面這個方法並不能適配圖片比例不一致的網站(好比本站),不過好在,爲了用戶體驗,如今絕大多數網站的圖片比例都有明確的要求,絕大多數狀況下咱們只適配保證網站經常使用的的幾種圖片寬高比例便可。

像Medium同樣懶加載圖片

Medium的懶加載圖片的體驗相信去 Medium 讀過文章的同窗都體驗過了,能夠說是很是的流暢。而其背後的技術其實也就是咱們上面講到的幾種技術的組合。
  1. 使用 aspect ratio box 建立佔位元素。
  2. 在html解析時只加載一個小尺寸的圖片,而且添加blur效果。
  3. 最後使用js選擇性的加載真實圖片。
Demo 以下 codePen by José M. Pérez

總結

  1. 懶加載用戶當前視窗中的圖片能夠提高頁面的加載性能。
  2. 懶加載的思路是在html解析時先加載一個placeholder圖片,最後再用js選擇性的加載真實圖片。
  3. 若是須要滾動加載可使用 Intersection Observer 。
  4. 對於固定尺寸和不定尺寸的圖片,咱們能夠選擇不一樣的服務去或者placeholder圖片。
  5. 對於圖片尺寸不肯定引發的佈局抖動問題咱們可使用 aspect ratio box 來解決。

參考資料

Progressive Loading
Lazy loading with responsive images and unknown height
Simple image placeholders for lazy loading images
How Medium does progressive image loading
Sizing Fluid Image Containers with a Little CSS Padding Hack

點個『在看』支持下 css

本文分享自微信公衆號 - 前端技術江湖(bigerfe)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。html

相關文章
相關標籤/搜索