图片懒加载原理 - 从简单到复杂


做者: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

相关文章
相关标签/搜索