前端JS图片懒加载原理方案详解

目录
  • 背景
  • 原理
  • 方案
    • 方案一:img的loading属性设为“lazy”
      • 使用方法
      • 优点
      • 兼容性
      • 缺点
    • 方案二:通过offsetTop来计算是否在可视区域内
      • 优化
      • 优点
      • 缺点
    • 方案三:通过getBoundingClientRect来计算是否在可视区域内
    • 方案四:使用IntersectionObserver来判断是否在可视区域内
      • 兼容性
      • 优点
      • 缺点
  • 问题
    • 布局抖动
    • 响应式图片
    • SEO不友好
  • 插件

背景

懒加载经常出现在前端面试中,是前端性能优化的常用技巧。懒加载也叫延迟加载,把非关键资源先不加载,等用到了再加载,将加载非关键资源的时间推迟,而加快页面的初始加载时间。懒加载经常被用在图片、视频、音频、JavaScript 文件等资源加载上,本文主要讨论图片的懒加载。通过本文你能收获:

  • 图片懒加载的原理
  • 实现图片懒加载的四种方案的原理以及优缺点
  • 图片懒加载的一些优化

原理

图片懒加载的原理是没有在可视区域的图片暂时不加载图片,等进入可视区域后在加载图片,这样可以减少初始页面加载的图片数量而提升页面加载速度。 图片懒加载在提升页面加载速度的同时也会伴随用户看其他未展示的图片时会有等待时间;图片加载显示会伴有布局抖动等问题。

方案

图片懒加载的关键是:判断一个元素是否在可视区域。

方案一:img的loading属性设为“lazy”

HTMLImageElement 的 loading 属性为一个字符串,它的值会提示 用户代理 告诉浏览器不在可视视口内的图片该如何加载。这样一来,通过推迟图片加载仅让其在需要的时候加载而非页面初始载入时立刻加载,优化了页面的载入。

lazy 告诉用户代理推迟图片加载直到浏览器认为其需要立即加载时才去加载。例如,如果用户正在往下滚动页面,值为 lazy 会导致图片仅在马上要出现在 可视视口中时开始加载。

使用方法

<img src="xxx.jpg" loading="lazy" />

优点

只设置一个属性不用 JavaScript 控制代码是最简单方便的方案,性能也是比较好的。

兼容性

大部分主流浏览器都兼容该属性。

缺点

虽然整个方案简单性能好,但问题也是最多的,所以很少使用这种方案。

  • 前面提到图片懒加载用户看其他未展示的图片时会有等待时间,一般会设置一个默认图片,这种方案不能设置默认图片
  • 图片的加载数量和图片的布局、可视区域尺寸有关,难以控制
  • 图片的加载顺序也难以控制

该方案能粗略的实现图片懒加载基本功能。

方案二:通过offsetTop来计算是否在可视区域内

可视区域高度是 document.documentElement.clientHeight ,而可视区域的位置是在滚动条滚动位置 scrollTopscrollTop+document.documentElement.clientHeight之间。因此通过 image.offsetTop <= document.documentElement.clientHeight + document.documentElement.scrollTop 判断图片是否可以在可视区域内。

  function lazyload() {
        var lazyImages = document.querySelectorAll(".lazyload");
        lazyImages.forEach(function (image) {
          if ( image.offsetTop <= document.documentElement.clientHeight + document.documentElement.scrollTop) {
            image.src = image.getAttribute("data-src");
          }
        });
      }

添加滚动条监听。

  window.onscroll = function () {
        lazyload();
};

html结构。

 <img src="./default.gif" class="lazyload" data-src="./photo-1.jpg" />

优化

上面只是简单的实现图片懒加载,在实际开发中还要很多细节需要优化: 首先是兼容性,这里有两个点涉及到兼容性:document.documentElement.clientHeightdocument.documentElement.scrollTop 。 获取浏览器窗口的内部高度方法有 window.innerHeightdocument.documentElement.clientHeightwindow.innerHeight兼容性是 ie9+ 和其他主流浏览器。

document.documentElement.clientHeight 浏览器都支持。

获取滚动位置方法有 window.pageYOffsetdocument.documentElement.scrollTopwindow.pageYOffset 兼容性是 ie9+ 和其他主流浏览器。

第二优化点是offsetTop。

offsetParent 元素有滚动条的情况下计算会不会有问题

HTMLElement.offsetTop 为只读属性,它返回当前元素相对于其 offsetParent 元素的顶部内边距的距离。 ——MDN

offsetTop 是相对其 offsetParent 元素的并不是相对浏览器窗口可视区域的。如果图片元素有 offsetParent 那么 offsetTop 是有偏差的。

      function getBoundingClientTop(el) {
        let top = el.offsetTop;
        let parent = el.offsetParent;
        while (parent) {
          top += parent.offsetTop;
          parent = parent.offsetParent;
        }
        return top;
      }

第三优化点避免赋值 src 。 代码是通过 lazyload 类获取需要懒加载的元素,这样会把之前已经加载图片的元素也获取到了,而重复设置 src属性。

   function lazyload() {
        var lazyImages = document.querySelectorAll(".lazyload[data-src]");
        lazyImages.forEach(function (image) {
          if (
            getBoundingClientTop(image) <=
            document.documentElement.clientHeight +
              document.documentElement.scrollTop
          ) {
            image.src = image.getAttribute("data-src");
            image.removeAttribute("data-src")
          }
        });
      }

通过 lazyload 类并且有 data-src 来获取元素,src 设置完后移除 data-src 属性来避免重复设置 src 。

第四优化点 onscroll 是否添加防抖。 onscroll 常用的优化点是加入防抖来减少事件触发的频率,但这里如果加了防抖,计算元素是否在可视区域内的精度就差很多,当滚动速度比较快的情况下加载反应不灵敏,这里就要找平衡点。

第五优化点页面中局部的 div 滚动图片懒加载。 除了整个页面的滚动图片懒加载,也有页面中局部滚动图片懒加载,就需要给制定的有滚动条 dom 元素绑定onscroll 事件。

    srcollDom.onscroll = function () {
        lazyload();
      };

并且获取图片 top 是相对有滚动条 dom 元素

getBoundingClientTop(image)-getBoundingClientTop(srcollDom) <= srcollDom.clientHeight + srcollDom.scrollTop

第六优化点加载图片的时间点提前。 代码中是图片元素进入可视区域后才加载图片,用户就需要等待一段时间才能看到图片显示出来,如果把图片加载时间提前,图片元素距离可视区域一定范围内就加载图片,那么用户等待时间就会减少一些。

优点

兼容性好,各个环节可以控制。

缺点

性能相对不是很好,滚动事件频繁触发,并且获取元素的位置信息,可能会强行触发重排和重绘导致一定的性能消耗。

方案三:通过getBoundingClientRect来计算是否在可视区域内

大致思路和方案二一样只是把获取图片元素 offsetTop 改成 getBoundingClientRect 方法获取离可视区顶端的距离。比方案一要简单一点,缺点也和方案一一样。

方案四:使用IntersectionObserver来判断是否在可视区域内

该方案是通过 IntersectionObserver 来判断图片元素是否在可视区域内。

IntersectionObserver() 构造器创建并返回一个 IntersectionObserver 对象。如果指定 rootMargin 则会检查其是否符合语法规定,检查阈值以确保全部在 0.0 到 1.0 之间,并且阈值列表会按升序排列。如果阈值列表为空,则默认为一个 [0.0] 的数组。 —— MDN

完全没看懂mdn的这段解释,简单说就是

IntersectionObserver 接口提供了一种异步观察目标元素与祖先元素或顶级文档 viewport 的交集中的变化的方法。祖先元素与视窗viewport被称为根(root)。

      var images = document.querySelectorAll(".lazyload");
      var io = new IntersectionObserver(
        function (entries) {
          entries.forEach((item) => {
            // isIntersecting是一个Boolean值,判断目标元素当前是否可见
            if (item.isIntersecting) {
              item.target.src = item.target.dataset.src;
              // 图片加载后即停止监听该元素
              io.unobserve(item.target);
            }
          });
        }
      );
      images.forEach(function(image){
        io.observe(image)
      })

IntersectionObserver 的监听对于页面局部滚动条也是有效的,不用再单独对局部滚动条进行处理。 而提前加载图片通过配置 rootMargin 来扩大监听区域。

      var images = document.querySelectorAll(".lazyload");
      var io = new IntersectionObserver(
        function (entries) {
          entries.forEach((item) => {
            // isIntersecting是一个Boolean值,判断目标元素当前是否可见
            if (item.isIntersecting) {
              item.target.src = item.target.dataset.src;
              // 图片加载后即停止监听该元素
              io.unobserve(item.target);
            }
          });
        },
        {
          root: null,
          rootMargin: "0px 0px 300px 0px",
        }
      );
      images.forEach(function(image){
        io.observe(image);
      })

兼容性

在一些低版本浏览器中还存在一些问题。

优点

性能好,简单。

缺点

低版本浏览器 IntersectionObserver 存在问题,不支持 IE。

问题

布局抖动

布局抖动是因为开始图片没有宽高,内容显示出来后有了宽高导致位置变动。带来的影响主要是用户体验不好,用户的注意力已经锁定了某个区域准备阅读,突然那个区域下移了,中断阅读而重新定位。可以直接在 img 标签上设置要加载图片的宽高。

<img src="blank.gif" data-src="normal.jpg" style="width:800px;height:600px;" />

解决问题:方案解决的问题范围是图片宽高固定的情况,在响应式环境图片宽高不确定下不适用。

用户体验:img的默认占位图是一个loading或是灰色背景,图片还没加载的体验。

响应式图片

虽然响应式下图片的宽高会变,但是图片的宽高比是不变的,图片的宽高比变了图片也就变形了。所以 img 标签设定图片宽高比,就能根据不同视图的宽度算出不同高度。 先创建一个宽高比为 5:1 的 div。

   <div style="padding-bottom: 20%;background-color: green;"></div>

padding 为百分比是相对自身宽度的百分比。 然后再创建了一个宽高比为 5:1 的 img。

    <div style="padding-bottom: 20%;position: relative;">
        <img style="position:absolute;width: 100%;height:100%">
      </div>

这样就能适应响应式的宽度改变,这种方式叫 Aspect Ratio Boxes。 占位图片可以设置成原图片的小尺寸图片,被放大后图片变模糊,这样开始加载小图片但图片的轮廓出现,后面在加载大图片显示清晰,给用户的体验是图片开始就在加载,然后加载完成就变清晰了。 img 标签 srcset 属性是处理响应式图片的。懒加载中可以设置 data-srcset 来延迟修改 srcset 属性。

SEO不友好

<img> 标签中的 src 属性携带的仍然是原始大小的图片确保了站外 SEO、社会化分享、RSS 等不会读不到原图。Aspect Ratio Boxes 方式使占位图片适应响应式,srcset 属性存放了一张原图的小尺寸缩略图阻止 src 原图的加载而加载缩略图优化加载体验,最后延迟将 data-srcset 的值赋值到 srcset 中。

插件

  • lazyload.js 是 IntersectionObserver 方式,而且当浏览器不支持 IntersectionObserver 的时候就直接加载图片,没有延迟加载的功能。
  • vue-lazyload 使用 IntersectionObserver 和 getBoundingClientRect 方式,默认 getBoundingClientRect 方式懒加载,里面的一些封装细节有很多有意思的地方,不止绑定了 onscroll 事件还绑定了 'onwheel'、'onmousewheel'、'onresize'、 'onanimationend'、'ontransitionend'、'ontouchmove'问什么要绑定这么多事件,插件为什么默认 getBoundingClientRect 方式而不用 IntersectionObserver 方式,待下回分解。
  • react-lazyload 只用了 getBoundingClientRect 方式,里面的封装细节也很有意思,待下回分解。

以上就是前端JS图片懒加载原理方案详解的详细内容,更多关于JS图片懒加载原理的资料请关注我们其它相关文章!

(0)

相关推荐

  • javascript实现图片预加载和懒加载

    本文实例为大家分享了javascript实现图片预加载和懒加载的具体代码,供大家参考,具体内容如下 预加载 预加载是预先加载好后面需要用到的资源, 后面使用的时候直接去缓存里取.举个栗子, 比如一个网站的开场动画, 这些动画是由很多图片组成的, 假如不预先加载好, 那就会造成动画不流畅产生闪动白屏.图片是提高用户体验的一个很好方法.图片预先加载到浏览器中,保证了图片快速.无缝地发布,使用户在浏览你网站内容时获得更好的用户体验. //这里我把图片数量写死了,而且对图片名也有要求必须是阿拉伯数字后缀

  • JS图片懒加载的优点及实现原理

    这篇文章主要介绍了JS图片懒加载的优点及实现原理,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 图片懒加载优势: 增强用户体验 优化代码 减少http的请求 减少服务器端压力 服务器的按需加载 图片懒加载原理: 先将图片的src设置为同一张图片或者不设置,同时给img标签设置一个特殊属性,例如:data-src用于存放图片的真实预览地址:若图片未进入可视区域时,展示同一张图片或者直接不展示图片,此时就不会发生http请求,当图片进入可视区域时,

  • 一文搞懂JavaScript如何实现图片懒加载

    目录 实现思路 准备知识 data-* getBoundingClientRect() throttle window.innerHeight 完整代码 js部分 CSS部分 运行结果 总结 图片懒加载,往往作为减少首页白屏时间的一个解决方案而出现.直观的来说,就是不要直接加载所有图片,而是满足一定条件后才加载,也就是”惰性加载“.实现图片懒加载的方式有很多,如果要简单点那就直接使用第三方插件:vue-lazyload,如果想探究一下别人的插件是怎么实现图片懒加载的,那么可以看看本文是如何实现的

  • JavaScript图片懒加载的优化方法详解

    目录 一.方法一 二.方法二 InterSectionObserver 总结 一.方法一 重点: 1.getBoundingClientRect().top > window.innerHeight 图片未出现 2.getBoundingClientRect().top < window.innerHeight 图片出现了 HTML: <ul> ...... <li>2222222222</li> <li>2222222222</li>

  • 详解JavaScript两个实用的图片懒加载优化方法

    目录 一.方法一 二.方法二 InterSectionObserver 一.方法一 重点: 1.getBoundingClientRect().top > window.innerHeight 图片未出现 2.getBoundingClientRect().top < window.innerHeight 图片出现了 HTML: <ul> ...... <li>2222222222</li> <li>2222222222</li> &

  • 前端JS图片懒加载原理方案详解

    目录 背景 原理 方案 方案一:img的loading属性设为“lazy” 使用方法 优点 兼容性 缺点 方案二:通过offsetTop来计算是否在可视区域内 优化 优点 缺点 方案三:通过getBoundingClientRect来计算是否在可视区域内 方案四:使用IntersectionObserver来判断是否在可视区域内 兼容性 优点 缺点 问题 布局抖动 响应式图片 SEO不友好 插件 背景 懒加载经常出现在前端面试中,是前端性能优化的常用技巧.懒加载也叫延迟加载,把非关键资源先不加载

  • JS图片懒加载库VueLazyLoad详解

    目录 背景 说明 实现原理 1. placeholder 的实现很细致和灵活 2. 添加图片缓存 3. 事件监听使用节流 4. 监听事件不止滚动事件 5. 事件列队的方式来处理懒加载 6. 支持 data-srcset 7. 自定义控制可视区的判定范围 待完善 1. 没有解决布局抖动 2. 跳过已经加载图片的判断方式 3. 局部懒加载 4. 性能不是很好 5. observer 模式配置简单 6. SEO 不友好 总结 背景 上篇<图片懒加载原理方案详解>中详细解析了图片懒加载的原理和方案.主

  • Vue自定义图片懒加载指令v-lazyload详解

    Vue是可以自定义指令的,最近学习过程中遇见了一个需要图片懒加载的功能,最后参考了别人的代码和思路自己重新写了一遍.以下将详细介绍如何实现自定义指令v-lazyload. 先看如何使用这个指令: <img v-lazyload="imageSrc" > imageSrc是要加载的图片的实际路径. 为了实现这个指令,我们首先单独建立一个文件,名字为lazyload.js.并填写基本的代码,如下: //Vue 图片懒加载,导出模块 export default (Vue , o

  • JS实现图片懒加载(lazyload)过程详解

    对于图片较多的页面,使用懒加载可以大幅提高页面加载速度,提高用户体验. 懒加载的意义(为什么要使用懒加载) 对页面加载速度影响最大的就是图片,一张普通的图片可以达到几M的大小,而代码也许就只有几十KB.当页面图片很多时,页面的加载速度缓慢,几S钟内页面没有加载完成,也许会失去很多的用户. 所以,对于图片过多的页面,为了加速页面加载速度,所以很多时候我们需要将页面内未出现在可视区域内的图片先不做加载, 等到滚动到可视区域后再去加载.这样子对于页面加载性能上会有很大的提升,也提高了用户体验. 原理

  • js 图片懒加载的实现

    1.使用场景 当网页上有大量图片需要加载时,如果一次性将图片全部加载完,网页加载时间会过长: 网页本身已经反应很慢了,如果你的页面上又需要引用图片,这时候同样是雪上加霜. 2.图片懒加载原理 图片懒加载,只不过是叫法比较高大上而已,其实现方式很简单,就是在需要的时候再给图片的src属性赋值,仅此而已. 3.代码实现 /** * 图片懒加载 */ function ImgLazyLoad() { /** * 滚动到图片所在位置再加载 * @param imgId * 懒加载图片的ID * @par

  • Swift 开发之懒加载的实例详解

    Swift 开发之懒加载的实例详解 /// A display link that keeps calling the `updateFrame` method on every screen refresh. private lazy var displayLink: CADisplayLink = { self.isDisplayLinkInitialized = true let displayLink = CADisplayLink(target: TargetProxy(target:

  • js前端实现图片懒加载(lazyload)的两种方式

    在实际的项目开发中,我们通常会遇见这样的场景:一个页面有很多图片,而首屏出现的图片大概就一两张,那么我们还要一次性把所有图片都加载出来吗?显然这是愚蠢的,不仅影响页面渲染速度,还浪费带宽.这也就是们通常所说的首屏加载,技术上现实其中要用的技术就是图片懒加载--到可视区域再加载. 思路: 将页面里所有img属性src属性用data-xx代替,当页面滚动直至此图片出现在可视区域时,用js取到该图片的data-xx的值赋给src. 关于各种宽高: 页可见区域宽: document.body.clien

  • 快速实现JS图片懒加载(可视区域加载)示例代码

    js懒加载图片 如何提高网页加载速度?在网页中有许多img标签,这些标签就是图片,其属性src则是指向服务器地址,当浏览器从上往下读取到src标签中的地址时,浏览器就会开启线程,加载这张图片.而并不是等到整张页面都解析完成才加载图片.我们要做的就是加载用户可视范围内的图片. js懒加载图片的目的 1.网页优化,提高网页加载速度 2.页面优化友好,提高SEO收录与排名 3.提高用户体验,减少服务器压力 实例代码如下: <!DOCTYPE html> <html lang="en&

  • JS图片懒加载技术实现过程解析

    懒加载技术 懒加载(LazyLoad)是前端优化的一种有效方式,极大的提升用户体验,图片一直是页面加载的流浪大户,现在一张图片几兆已经是很正常的事,远远大于代码的大小. 原理:页面加载后只让文档可视区内的图片显示,其它不显示,随着用户对页面的滚动,判断其区域位置,生成img标签,让到可视区的图片加载出来. 所用相关技术:给img加属性 (例如data-src),将图片的地址赋值给他,这样就生成img标签后再把data-src的值赋给img的src(通过dataset.src或者getAttrib

随机推荐