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

用IntersectionObserver优化图片加载

2021.01.29

715


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

相关新闻

JS函数节流和函数防抖

2018.09.14

2149

JS函数节流和函数防抖的原理与应用场景

Go语言并发

2019.12.31

1289

Go语言的并发是基于 goroutine 的,goroutine 类似于线程,但并非线程。可以将 goroutine 理解为一种虚拟线程。Go语言运行时会参与调度 goroutine,并将 goroutine 合理地分配到每个 CPU 中,最大限度地使用 CPU 性能。

开发APP,OUT了,微信小程序

2016.09.26

7054

张小龙在朋友圈里这样解释道:小程序是一种不需要下载安装即可使用的应用,它实现了应用「触手可及」的梦想,用户扫一扫或搜一下即可打开应用。也体现了「用完即走」的理念,用户不用关心是否安装太多应用的问题。应用将无处不在,随时可用,但又无需安装卸载。