vue中使用IntersectionObserver实现界面懒加载,加快首屏渲染速度

1,491 阅读2分钟

背景介绍:

随着当前项目的越来越大,某些界面的模块越来越多了,导致进入页面的加载速度很满。所以想做个界面的懒加载,加快一下进入界面的速度。

界面可能像下面这样。要实现模块的懒加载

image.png

怎么实现

思路

所有模块在初始化的时候先不调用初始化的方法。加载一个简单的静态页面。
在内容区域的mounted方法里面的添加IntersectionObserver方法,去监听个个模块是否在可视区域,如果在就初始化模块,取消监听,防止多次初始化。

IntersectionObserver介绍

IntersectionObserver是一个用来判断两个元素是否重叠的api。
支持两个参数IntersectionObserver(callback, options)。callback是监听元素的可见性变化时触发的回调函数;options是配置参数,可选。

callback参数

//observer 回调函数
const observerCallback = (entries) => {
    entries.forEach(item => {
       /*
        * item.time当可视状态变化时,状态发送改变的时间戳,ms
        * item.rootBounds:根元素矩形区域的信息,即为getBoundingClientRect方法返回的值
        * item.boundingClientRect:目标元素的矩形区域的信息。
        * item.intersectionRect:目标元素与视口(或根元素)的交叉区域的信息.
        * item.isIntersecting:目标元素与根元素是否相交
        * item.intersectionRatio:目标元素的可见比例,即intersectionRect占boundingClientRect的比例。
        * item.target:目标元素。
        */	
    })
}

options配置:

{
    root //表示监听的可视区域dom,默认为body
    threshold //目标元素与视窗重叠的阈值(0~1)
    rootMargin // 类似于margin,可以加大root的区域
}

整体实现

父级元素

<template>
  <div ref="moduleMain">
    <ModuleItem></ModuleItem>
    <ModuleItem></ModuleItem>
    <ModuleItem></ModuleItem>
    <ModuleItem></ModuleItem>
    <ModuleItem></ModuleItem>
    <ModuleItem></ModuleItem>
    <ModuleItem></ModuleItem>
    <ModuleItem></ModuleItem>
    <ModuleItem></ModuleItem>
  </div>
</template>

<script>
import ModuleItem from "./ModuleItem.vue";
export default {
  name: "intersectionObserverTest",
  components: {
    ModuleItem,
  },
  data() {
    return {};
  },
  mounted() {
    // 获取所有待观察的目标元素
    let headers = this.$refs.moduleMain.querySelectorAll(".module-item");
    var options = {
      threshold: 0.5, //目标元素与视窗重叠的阈值(0~1)
      root: null, // 目标视窗即目标元素的父元素,如果没有提供,则默认body元素
    };
    function lazyLoad(target) {
      const observer = new IntersectionObserver((entries, observer) => {
        entries.forEach((entrie) => {
          if (entrie.isIntersecting) {
            const head = entrie.target;
            head.__vue__.init();
            observer.unobserve(head); // 停止监听已加载的模块,防止多次加载
          }
        });
      }, options);
      observer.observe(target);
    }
    headers.forEach(lazyLoad);
  },
};
</script>

子元素

<template>
  <div class="module-item">2222</div>
</template>

<script>
export default {
  name: "ModuleItem",
  data() {
    return {};
  },
  methods: {
    init() {
      console.log('当前在视图范围内,初始化界面');
    }
  }
}
</script>

<style scoped>
.module-item {
  width: 100%;
  height: 300px;
}
</style>

关键地方讲解

// 获取所有待观察的目标元素,这里所有的dom都有相同的class,用这个来获取
let headers = this.$refs.moduleMain.querySelectorAll(".module-item");
var options = {
  threshold: 0.5, //目标元素与视窗重叠的阈值(0~1)
  root: null, // 目标视窗即目标元素的父元素,如果没有提供,则默认body元素
};
function lazyLoad(target) {
  const observer = new IntersectionObserver((entries, observer) => {
    entries.forEach((entrie) => {
      // isIntersecting代表是否相交,这里表示到视口区域内了
      if (entrie.isIntersecting) {
        const head = entrie.target;
        // head为dom,需要用__vue__才能调用内部方法,可以打debugger看看
        head.__vue__.init();
        observer.unobserve(head); // 停止监听已加载的模块,防止多次初始化
      }
    });
  }, options);
  observer.observe(target);
}
headers.forEach(lazyLoad);

最终效果

随着界面的滚动,下放的模块触发初始化事件,实现懒加载。

image.png