首页 前端知识 vue实现图片懒加载的五种方式

vue实现图片懒加载的五种方式

2024-07-20 17:07:41 前端知识 前端哥 18 591 我要收藏

推荐方法一,也就是使用插件

原理

存储图片的真实路径,把图片的真实路径绑定给一个以data开头的自定义属性data-src即可,页面中的img元素,如果没有src属性,浏览器就不会发出请求去下载图片(没有请求就提高了性能)

 <div class="scrollLoading" data-src="loaded.html">加载中...</div>

设置img的默认src为其他图片(所有的img都用这一张,只会发送一次请求

   <img  data-src="xxx" src="1px.gif" width="180" height="180"/>

需要一个滚动事件,判断元素是否在浏览器窗口,一旦进入视口才进行加载,当滚动加载的时候,就把这张图片替换为真正的url地址(也就是data-src里保存的值)
等到图片进入视口后,利用js提取data-src的真实图片地址赋值给src属性,就会去发送请求加载图片,真正实现了按需加载

实现图片懒加载的多种方法的区别就在于判断元素是否进入视口的方式不同,当使用方法四和方法五时,如果发生滚动事件,会产生大量的循环和判断操作判断图片是否可视区里,因此需要用节流进行优化。

方法一 lazyload插件

vue2

github地址:GitHub - hilongjw/vue-lazyload: A Vue.js plugin for lazyload your Image or Component in your application.

原理:Vue-lazyload原理详解之源码解析
vue-lazyload的核心原理是利用了IntersectionObserverAPI,这是一个用于检测元素是否与视口相交的API,它可以高效地监听元素的可见性变化,并触发回调函数。
vue-lazyload在注册插件时,会创建一个全局的IntersectionObserver实例,并设置一些选项,如阈值(threshold)、根元素(root)等。然后,在绑定v-lazy指令时,会创建一个监听器(listener)对象,并将其添加到一个监听器队列(listenerQueue)中。每个监听器对象都包含了元素的相关信息,如状态(state)、图片地址(src)等。
接下来,vue-lazyload会遍历监听器队列,并调用IntersectionObserver实例的observe方法,将每个元素注册到观察者中。当元素与视口相交时,IntersectionObserver实例会触发回调函数,并传入一个entries参数,表示所有被观察的元素的状态信息。vue-lazyload会根据entries中的isIntersecting属性判断元素是否可见,如果是,则调用监听器对象的load方法,将元素的src或者style属性替换为真实的图片地址,并将该监听器对象从队列中移除。

基本用法

安装

npm i vue-lazyload@1.2.3 -S

main.ts
// 1.图片懒加载插件

import VueLazyload from 'vue-lazyload'

// 2.注册插件

Vue.use(VueLazyload, {
  //参数配置 可不填
  
  // 懒加载默认加载图片
  loading: 'xxx.png',
  // 加载失败后加载的图片
  error: 'xxx.png',
  preLoad: 1.3, // 预加载高度的比例
  attempt: 3 // 尝试加载次数
})

vue-lazyload提供了一个自定义指令v-lazy,可以在img标签或者任何需要设置背景图片的标签上使用它。例如:

<!-- 懒加载img标签 -->
<img v-lazy="imgUrl" />

<!-- 懒加载背景图片 -->
<div v-lazy:background-image="bgUrl"></div>

v-lazy指令接收一个字符串类型的值,表示图片的地址。如果是背景图片,需要在指令后加上:background-image修饰符(注意冒号)。
当页面滚动时,vue-lazyload会检测元素是否进入可视区域,如果是,则替换元素的src或者style属性,从而实现懒加载。
注意:若图片为循环渲染、分页显示,则必须写key值,不然切换页面后页面视图不刷新

vue3

github地址:GitHub - caozhong1996/vue-lazyload-next: vue-lazyload for vue3
原理与使用方法与vue2版本插件相同,仅安装不同
安装

npm i vue-lazyload-next -S

main.ts

import VueLazyloadNext from 'vue-lazyload-next';
app.use(VueLazyloadNext, {
  // 添加一些配置参数 可不填
  
  // 懒加载默认加载图片
  loading: 'xxx.png',
  // 加载失败后加载的图片
  error: 'xxx.png',
  preLoad: 1.3, // 预加载高度的比例
  attempt: 3 // 尝试加载次数
});

方法二 IntersectionObserve()

API:IntersectionObserver - Web API 接口参考 | MDN

原理

Intersection
Observer是是浏览器原生提供的构造函数,使用它能省到大量的循环和判断。这个构造函数的作用是它能够观察可视窗口与目标元素产生的交叉区域。简单来说就是当用它观察我们的图片时,当图片出现或者消失在可视窗口,它都能知道并且会执行一个特殊的回调函数,就可以利用这个回调函数实现需要的操作。

hook

vue3中选择用hook进行集成
useLazyload.ts文件

// 定义自定义指令
const defineDirective = (app: any) => {
  app.directive('lazy', {
    mounted(el: HTMLImageElement, bindings: any) {
      // el表示使用指令的DOM元素
      // 指令的功能:实现图片的懒加载
      // 1、监听图片是否进入可视区
      const observer = new IntersectionObserver(([{ isIntersecting }]) => {
        // true;进入可视区域,false:未进入可视区域
        if (isIntersecting) {
          // 1、给图片的src属性赋值图片的地址
          el.src = bindings.value;
          // 2、取消图片的监听,默认是会一直监听的,如果不取消。就会一直执行
          // eslint-disable-next-line spellcheck/spell-checker
          observer.unobserve(el);
        }
      });
      // 监听dom元素
      observer.observe(el);
    }
  });
};
export default {
  install(app: any) {
    // 自定义指令
    defineDirective(app);
  }
};

基本用法
main.ts

import lazy from './hooks/useLazy';

app.use(lazy);

对需要进行懒加载的img标签,直接将:src替换为v-lazy

<img
  class="image"
  :key="data.rendered_image_url"
  v-lazy="data.rendered_image_url"
/>

注意:若图片为循环渲染、分页显示,则必须写key值,不然切换页面后页面视图不刷新

方法三 loading=“lazy”

HTML新增 loading属性
基本用法

<img src="xxx.png" loading="lazy">

loading 属性支持 3 种属性值:

  • auto 浏览器默认的懒加载策略,和不增加这个属性的表现一样
  • lazy 在资源距当前视窗到了特定距离内后再开始加载
  • eager 立即加载,无论资源在页面中什么位置
    图片必须声明width和height,不然会看到布局发生移动

兼容性
Lazy loading via attribute for images & iframes

方法四 滚动监听+scrollTop+offsetTop+innerHeight

如何判断元素是否到达可视区域

  • window.innerHeight 是浏览器可视区的高度;
  • document.body.scrollTop || document.documentElement.scrollTop是浏览器滚动的过的距离;
  • imgs.offsetTop 是元素顶部距离文档顶部的高度(包括滚动条的距离);
  • 内容达到显示区域: img.offsetTop < window.innerHeight + document.body.scrollTop;
  • 到达可视区域后,imgs[i].src = imgs[i].getAttribute('data-src') as string;将data-src属性值赋值给src,实现懒加载

但在集成过程中发现,由于img父元素标签没有并且无法设置高度,scrollTop始终为0,无法实现动态计算window.innerHeight + document.body.scrollTop的值

方法五:滚动监听+getBoundingClientRect()

API:Element.getBoundingClientRect() - Web API 接口参考 | MDN

//获取所有img标签
const imgs = document.getElementsByTagName('img');

onMounted(() => {
   //用于首屏加载
  lazyLoad();
  //添加滚动事件监听
  document.addEventListener('scroll', throttle(lazyLoad, 500), true);
});
onUnMounted(() => {
  document.removeEventListener('scroll', throttle(lazyLoad, 500), true);
});

// 节流
const throttle = (fn: { apply: (arg0: any, arg1: any[]) => void }, t: number) => {
  let flag = true;
  const interval = t || 500;
  return function (this: any, ...args: any) {
    if (flag) {
      fn.apply(this, args);
      flag = false;
      setTimeout(() => {
        flag = true;
      }, interval);
    }
  };
};

const lazyLoad = () => {
  const offsetHeight = window.innerHeight || document.documentElement.clientHeight;
  Array.from(imgs).forEach(item => {
    const oBounding = item.getBoundingClientRect(); //返回一个矩形对象,包含上下左右的偏移值
    if (0 <= oBounding.top && oBounding.top <= offsetHeight) {
      //     //性能优化 进行判断 已经加载的不会再进行加载
      if (item.getAttribute('alt') !== 'loaded') {
        item.setAttribute('src', item.getAttribute('data-src') as string);
        item.setAttribute('alt', 'loaded');
      }
    }
  });
};

缺点

  • 挂载时需要立刻调用lazyLoad函数,不然首屏不会加载
  • 若需要图片分页,切换分页后不会也主动加载,需要调用lazyLoad方法
  • 同样需要给图片设置key值,不然分页图片不刷新
转载请注明出处或者链接地址:https://www.qianduange.cn//article/14010.html
标签
评论
发布的文章

JQuery中的load()、$

2024-05-10 08:05:15

大家推荐的文章
会员中心 联系我 留言建议 回顶部
复制成功!