前端性能优化:封装一个通用的懒加载组件

在当今的互联网时代,用户体验至关重要。为了提高用户体验,开发人员不断探索各种技术和方法,其中懒加载技术是一种常见的方法。本文将介绍如何使用一个简单的懒加载容器组件来实现页面内容的懒加载,从而提高用户体验。

实现原理

本文懒加载组件的实现原理是通过IntersectionObserver实例来监听页面元素是否进入可视化窗口,只有当元素可见时,才初始化组件,避免了不必要的资源浪费。

IntersectionObserver是一个由浏览器提供的JavaScript接口,它能够追踪元素与视窗的交互情况。此API能够告知开发者,某个元素是否正处于视窗内或正离开视窗,同时还能提供关于两者相交部分的详细信息,包括大小和具体位置。

特点

  • 非阻塞性质:该 API 以异步方式运行,利用浏览器的高效内部处理机制,不会影响到主线程的运行,有效避免了性能瓶颈。
  • 资源高效:与传统的滚动事件监听或定时器相比,IntersectionObserver 能够精确监测元素与视窗的相交情况,减少了不必要的计算和回调函数的触发,从而降低了资源消耗。
  • 多元素监控:IntersectionObserver 允许同时对多个元素进行监控,通过回调函数逐一通知开发者在视窗中的相交状态,便于实现批量处理。
  • 阈值自定义:开发者可以设置一个或多个阈值,以定义元素与视窗相交的程度。一旦相交比例达到或低于这些阈值,便会激活相应的回调函数。

利用 IntersectionObserver,开发者能够轻松实现诸如图片懒加载、滚动加载更多内容、广告可见性控制等功能,这些都能够显著提高网页的性能和用户的互动体验。

官方api:交叉观察器 API - Web API 接口参考 | MDN

效果图

例如下方图中转圈的卡片是页面滚动条往下拉后刚出现在页面中的,表示正在调用接口和初始化操作,上面没有转圈的表示已经渲染完毕的卡片。

组件代码

懒加载组件LazyContainer的代码如下:

js 复制代码
<template>
  <div ref="container">
    <slot v-if="isInit"></slot>
    <!--组件初始化之前显示加载态loading-->
    <div key="skeleton" v-else style="height: 100%; width: 100%">
      <!-- 如果提供了骨架屏插槽,则显示它 -->
      <slot name="skeleton" v-if="$slots.skeleton"></slot>
      <!-- 否则显示默认的加载态 -->
      <div v-else class="skeleton-item"></div>
    </div>
  </div>
</template>
<script setup defer>
  import { onMounted, onUnmounted, ref } from 'vue';

  const container = ref(null);
  const isInit = ref(false);
  const timer = ref();
  onMounted(() => {
    // 创建一个Intersection Observer对象来观察元素是否进入视口
    const observer = new IntersectionObserver((entries) => {
      // 如果元素进入视口,设置定时器,800毫秒后设置isInit为true
      if (entries[0].isIntersecting) {
        timer.value = setTimeout(() => {
          isInit.value = true;
          // 在组件卸载时取消观察
          observer.unobserve(container.value);
        }, 800);
      }
    });
    // 开始观察container元素
    observer.observe(container.value);
  });
  onUnmounted(() => {
    clearTimeout(timer.value);
  });
</script>
<style scoped>
  /*样式二*/
  .skeleton-item {
    width: 100%;
    height: 100%;
    background-color: #f3f3f3;
    border-radius: 10px;
    overflow: hidden;
  }

  .skeleton-item:empty {
    background-color: #f2f2f2;
  }

  .skeleton-item:empty::before {
    /* 内容区域空状态时追加骨架屏样式 */
    content: '';
    display: block;
    width: 100%;
    height: 100%;
    transform: translateX(-100%);
    background: linear-gradient(90deg, transparent, rgba(225, 225, 225, 0.753), transparent);
    animation: loading 1s infinite;
  }

  @keyframes loading {
    100% {
      transform: translateX(100%);
    }
  }
</style>

调用方式

test_div是需要被懒加载的组件或元素

ts 复制代码
<template>
    <div class="lazyCard">
      <div v-for="(item, index) in 50" :key="index" style="height: 500px">
        <lazy-container style="height: 200px;width: 200px">
          <div class="test_div"></div>
        </lazy-container>
      </div>
    </div>
</template>
<script lang="ts" setup>
  import LazyContainer from '@/components/LazyComponents/LazyContainer.vue';
</script>
<style scoped lang="less">
  .lazyCard {
    height: 800px;
    width: 800px;
    padding: 10px;
    margin: 0;
    display: grid;
    border-radius: 5px;
    box-shadow: 0px 0px 12px rgba(0, 0, 0, 0.12);
    grid-template-columns: repeat(3, 1fr);
    overflow: auto;
  }
  .test_div {
    height: 200px;
    border: 1px solid #e0e3ea;
    background-color: #7b7d80;
    border-radius: 10px;
  }
</style>

当页面存在多个test组件时,不使用懒加载,将会同时渲染,同时调用接口,会造成页面的卡顿,非常影响体验。而使用了懒加载后,只有当进入页面可视区域的test组件才会初始化渲染,提高页面加载速度。

注意点

LazyLoader组件中的setTimeout设置了一个延迟时间,确保在组件初始化之前显示一个加载状态框,避免了显示空白区域的突兀感。同时,懒加载组件的宽度和高度需要与监听的组件高度宽度一致,这样加载时loading态的骨架屏才会和需要懒加载的组件大小一致。

相关推荐
mldong3 小时前
你的 Vue3 项目也能有钉钉同款审批流设计器:npm 装包,10 分钟画出第一条审批流
前端·vue.js
2分钟速写快排3 小时前
什么是 RAG?如何用 RAG 实现一个用户记忆?
前端·后端·ai编程
passerby60614 小时前
如何自己造一个时间处理库
前端·javascript·github
走到天涯海角5 小时前
react里面的长列表渲染优化
前端·react.js·前端框架
小羊没烦恼!5 小时前
Hello Web API系列教程——Web API与国际化
java·服务器·前端·javascript·php
北岛贰5 小时前
迷茫焦虑期,我做了一个带支付带官网的 AI 聊天虚拟恋人 App
前端·人工智能·后端
mayaairi7 小时前
Vue2 组件通讯(三):全局事件总线、PubSub、插槽与组件实例属性
前端·javascript·vue.js
kyriewen7 小时前
面试官问我:AI 都能写代码了,前端凭什么还值 25K
前端·javascript·人工智能
风骏时光牛马8 小时前
AI源码分析:拆解模型底层实现逻辑
前端
IT_陈寒8 小时前
React子组件莫名其妙重渲染?你可能漏了这个Hook
前端·人工智能·后端