您所在的位置:首页 / 知识分享

用IntersectionObserver优化图片加载

2021.01.29

971


使用IntersectionObserver优化图片加载

前言

工作中经常通过判断元素是否进入视口,一般有三种方式进行判断

1. el.offsetTop - document.documentElement.scrollTop <= viewPortHeight
2. el.getBoundingClientRect().top <= viewPortHeight
3. intersectionRatio > 0 && intersectionRatio <= 1 复制代码

根据元素与视口是否相交,可以进行吸顶、吸底、曝光上报、列表加载更多、图片懒加载等操作。

问题

前面两种需要通过监听scroll事件,为了防止频繁触发,需要做防抖处理。

用户体验

  • 当元素进入视口时,总是需要延迟一定时间才能执行判断逻辑。

性能

  • 在主线程上运行,因此频繁触发、调用会造成性能问题
  • 无论是否触发相交,滚动结束后都会进行判断
  • 获取srollTop的值和getBoundingClientRect方法都会导致回流
  • 滚动事件会绑定多个事件处理函数,阻塞UI渲染

IntersectionObserver

当目标元素和根元素相交或失交,并且当前 tick 期间没有任务运行时,回调函数就会被执行

构造函数

let observer = new IntersectionObserver(callback, options); 复制代码

callback

每个entry描述一个观察到的目标元素的交集变化

let callback = (entries, observer) => {
  entries.forEach(entry => { // entry.boundingClientRect // 目标元素的区域信息,getBoundingClientRect()的返回值 // entry.intersectionRatio // 目标元素的可见比例 // entry.intersectionRect // 目标元素与根元素交叉的区域信息 // entry.isIntersecting // 目标元素是否进入根元素区域 // entry.rootBounds // 根元素的矩形区域信息 // entry.target // 被观察dom节点 // entry.time // 相交发生时距离页面打开时的毫秒数 });
}; 复制代码

options

  • root根元素,不指定默认为视窗
  • rootMargin根元素的外边距
  • threshold目标元素与根元素相交比例达到该值触发回调

实例方法

  • observe开始监听一个目标元素(target),target必须是root的后代
  • unobserve停止监听一个目标元素
  • takeRecords返回所有监听的目标元素集合
  • disconnect停止所有监听

参考

MDN Web Docs - Intersection Observer API

vue directive优化懒加载

步骤

防止直接加载图像,先把图片地址存放在data-src上

 复制代码

LazyLoadDirective.js

export default { // hookFunction 当绑定元素插入父节点时调用 insert(el) { function loadImage() {
      el.addEventListener('load', () => { // 加载完成后延迟添加class可以实现淡入动画 setTimeout(() => {
          el.classList.add('loaded')
        }, 100);
      }); // 加载 data-url 的图片地址 el.src = el.dataset.url;
    } function handleIntersect(entries, observer) {
      entries.forEach(entry => { if (!entry.isIntersecting) { return;
        } else { // 绑定元素进入视口后触发加载图片 loadImage(); // 并停止观察可见性变化, 防止再次加载图像。 observer.unobserve(el);
        }
      });
    } function createObserver() { const options = { root: null, threshold: '0' }; const observer = new IntersectionObserver(handleIntersect, options); // 订阅观察当前绑定图片元素 observer.observe(el);
    }
    
    createObserver();
  }
} 复制代码

思考

如果使用scroll事件来判断图片可见或不可见,每次需要重新计算的几十个图像,显然使用IntersectionObserver更加优雅。

注册指令

// main.js 全局注册 import Vue from 'vue'; import LazyLoadDirective from '@/directives/LazyLoadDirective';

Vue.directive('lazyload', LazyLoadDirective); 复制代码

小结

使用IntersectionObserver实现延迟加载非常简单,拥有很好的性能和用户体验,目前系统兼容性覆盖的比较广。当然,如果列表元素节点特别多时,也需要进行长列表优化。

相关新闻

平面小课堂!9种最常见的排版布局分割技巧

2018.10.09

3241

9种最常见的排版布局分割里的讲究。

从ES6到ES10的新特性万字大总结(不得不收藏)

2019.12.19

1388

ECMAScript是一种由Ecma国际(前身为欧洲计算机制造商协会)在标准ECMA-262中定义的脚本语言规范。这种语言在万维网上应用广泛,它往往被称为JavaScript或JScript,但实际上后两者是ECMA-262标准的实现和扩展。