懒加载的新加载方式(转)
还不错的一篇文章,看到了分享一下
懒加载的新加载方式(转)
还不错的一篇文章,看到了分享一下
“懒加载”不是一个新的概念,对于经验丰富的你们来说,一定积累了很多实现方法,但为什本文还要提出来呢?因为我们开发过程中,常用的实现方式都是通过监听页面的 scroll 事件来实现的,这种方法在使用过程中,scroll 事件会被高频触发,强制浏览器重排和重绘,从而不断增加浏览器的压力,导致浏览器性能的损失,有时可能出现卡顿或是闪烁的现象,即使使用了节流与去抖等方法,但还是会产生高昂的计算开销。那是否有更好的方法来解决呢,本文会讲解一种新实现方式,在此之前,先让我们回顾一下常用的实现姿势吧!
通过 Element.getBoundingClientRect() 方法可以返回元素的大小及其相对于视口的位置。 rectObject = object.getBoundingClientRect() ;返回值 rectObject 对象包含了一组用于描述边框的只读属性—— left 、top 、right 和 bottom ,单位为像素。除了 width 和 height 外的属性都是相对于视口的左上角位置而言的。 如下图:
随着页面滚动,我们可以获取懒加载元素相对于视口的顶点位置 rectObject.top 的像素值,当这个值小于可视区高 window.innerHeight ,表示已经进入可视区,开始加载数据,如果要提前加载,可以设置一个 threshold 阈值。
首先我们获取元素 Element.offsetTop ,获取可视区高度 window.innerHeigh 。 随着页面滚动,实时获取滚动条的高度 scrollTop ,如果元素 Element.offsetTop – 滚动条的高度 scrollTop 小于可视区高度 window.innerHeight 的时候,开始加载数据。
这两种实现方式都是通过监听页面的 scroll 事件的方法来实现,一直以来,我们都没有找到一个完美可靠的解决办法,试想,如果有一个方法,可以在指定的懒加载元素进入可视区域就通知我们就好了。 今天我就和大家介绍一个厉害的 API (IntersectionObserver API) ,它可以为我们提供一个简单直接的办法,让我们知道一个被观测的元素什么时候进入或离开可视区的视口。
简单介绍一下,截取 MDN [1]上的介绍:
” IntersectionObserver 接口(从属于 Intersection Observer API )为开发者提供了一种可以异步监听目标元素与其祖先或视窗( viewport )交叉状态的手段。”
也就是说这个 API 提供了一种方法,可以异步观察目标元素与祖先元素或顶层文件的交集变化。网站不需要为了监听两个元素的交集变化而在主线程里面做任何多余的操作,浏览器就可以帮助我们优化和管理两个元素的交集变化, 且它是一个异步的 API,也就是说只有线程空闲下来,才会执行观察器。
说了这么多,你可能很想知道,这个API能否在实际项目中应用,下面我们先来看一下 IntersectionObserver API 的浏览器支持情况。
大家可以在caniuse [3]上面查看最新的结果。
尽管现在还没有达到全部浏览器的支持。但是,如果你想尝试使用,并支持一些未支持的浏览器,可以使用 WICG 提供的 polyfill 帮大家解决这个问题。但值得的注意的是,使用 polyfill 是无法获取到原生实现带来的性能优势的。如果你很在意,或许,你也可以选择判断浏览器是否支持,从而对不支持的浏览器使用其他方法去实现。
在正式使用之前,我们先简单了解一下 API 中所有的参数和实例方法。以便我们在后续例子的讲解。
创建构造函数:
1 2 3 4 5 | new IntersectionObserver(callback, options) callback === (entries, observe) => { console.log(entries) } |
从上面可以看到,IntersectionObserver支持两个参数:
(1)callback, 触发的回调函数,当被观察元素与根元素视口发生交集,或交集部分大小发生变化的时候被执行,返回每次相交时的交集信息。接受两个参数,第一个参数是交集信息,以 IntersectionObserverEntry 对象数组形式返回,第二个是观察者本身。下面先来打印一下交集的返回信息。
我们看到,IntersectionObserverEntry 对象有6个属性:
可以简单用一张图来标注一下。
(2)options ,配置对象,包含三个可选属性,一旦 IntersectionObserver 被创建,则无法更改其配置。
API提供了四个实例方法:
利用 IntersectionObserver 实现懒加载页面模块,把每个模块都作为一个单独的目标元素去观察,当被观察的目标元素进入根元素的可视区内,开始加载组件模块内容。先看一下实现的效果。
我们把懒加载需求抽成一个单独的组件,在未加载的时候设置了占位骨架,可以自定义显示样式。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | <template> <transition :name="fade"> <div v-if="isLoad"> <slot></slot> </div> <div v-else> <slot name="skeleton"></slot> </div> </transition> </template> <script> export default { data() { return { isLoad: false, observer: null ... }; }, mounted() { ... } |
被观察的目标元素是 dom 元素,且使用的 vue 框架开发,所以需要在 mounted 生命周期在实例化构造函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | mounted() { // 观察根元素和被观察目标元素的交叉情况 this.observer = new IntersectionObserver((entries, observer) => { entries.forEach((entry) => { if (entry.isIntersecting || entry.intersectionRatio) { // isLoad参数用于判断数据是否加载的,如果未加载值设置为 true ,开始后加载模块数据 if (!this.isLoad) { this.isLoad = true; // 停止观察目标元素 this.observer.unobserve(this.$el); } } }) }, { // 因为我们设置了50px的偏移量,所以只要被观察的元素靠近可视区50px,就会加载。 rootMargin: '50px 0px', root: null, threshold: [0, 0.01] }); // 观察目标元素 this.observer.observe(this.$el); } |
组件销毁的时候,要记得关闭观察器
1 2 3 4 5 6 | beforeDestroy() { // 停止观察所有的目标元素 if (this.observer) { this.observer.disconnect(); } } |
接下来,直接在页面中调用就好了,因为项目中是需要支持一些低版本浏览器的,所以用到了前面提到了 polyfill 来解决。
1 2 3 | npm install intersection-observer //安装之后在 .vue 页面 import 即可。 import 'intersection-observer'; |
如果你使用的其他技术栈,也可以参考https://github.com/w3c/IntersectionObserver/tree/master/polyfill上的使用方式。
发生以下几种情况,观察器将停止观察目标元素:
IntersectionObserver 还可以应用到 iframe 页面, 帮助我们检测 iframe 是否出现在的视口里。再此之前,对于跨域的 iframe ,我们是无法判断的。如果你想在 iframe 中使用,记得根元素一定要要设置成 null 或iframe 的祖先元素哦!
您的鼓励是我前进的动力---
使用微信扫描二维码完成支付