前端必会的图片加载方法

前言

      前端图片显示加载是网站很重要的一部分,很多网站图片占用了很大的空间,网站的性能的好坏也是给用户不同的体验,好的体验感会增加用户的“幸福感”。现在很多网站中图片往往占用了很大的资源,如果图片过大,加载效率低下,占用更多资源,加载显示不完整,使用者的观感上会不舒服。那么就让我们简单了解一下前端图片加载都会用到哪些方法。

基本使用方式

<img src="https://img95.699pic.com/photo/50044/9004.jpg_wh300.jpg" alt="xxx">

      一般我们页面显示图片都是img标签显示,这样是最直接的方式,但也是存在很多问题。如果当前页面图片很大、图片很多、网络也不是很好,那我们可能就会看到有些图片显示不完整,有些显示一部分或者是空白页。那我们就会采取相应的策略进行改善。

滚动监听加载懒加载

示例直接采用监听页面滚动,进行位置计算,当页面滚动到相应的位置我们加载对应的图片内容。

<div>
  <img class="lazy-load" src="https://source.unsplash.com/random/600" alt="">
  <img class="lazy-load" src="https://source.unsplash.com/random/700" alt="">
  <img class="lazy-load" src="https://source.unsplash.com/random/800" alt="">
  <img class="lazy-load" src="https://source.unsplash.com/random/900" alt="">
</div>
<style>
  div {
    margin-top: 350px;
  }
  .lazy-load {
    width: 200px;
    height: 150px;
  }
</style>
// 引入 lodash 库
<script src="https://cdn.bootcss.com/lodash.js/4.17.12-pre/lodash.core.min.js"></script>  
let lazyImages = [...document.querySelectorAll('.lazy-load')]
let inAdvance = 300
function lazyLoad() {
    lazyImages.forEach(image => {
        if (image.offsetTop < window.innerHeight + window.pageYOffset + inAdvance) {
            image.src = image.dataset.src;   // 替换真实图片的  URL
        }
    })
}
lazyLoad();
window.addEventListener('scroll', _.throttle(lazyLoad, 50))
window.addEventListener('resize', _.throttle(lazyLoad, 50))

现在京东,淘宝等pc商城采用这种方式,通过监听当前可视区域是否存在未加载好的图片,如果没有则进行加载。下面这种方式也是滚动懒加载的一种方式。

Intersection Observer API

      现在,有一个 Intersection observer 接口可以方便我们操作,它可以异步观察目标元素与祖先元素或顶层文件的交集变化。简单的说,以前我们需要自己去写滚动监听事件函数,现在,这个 API 可以帮助我们,我们只需要统一写一个观察函数 ,每当想观察的元素进入视口,也就是我们看见它时,就执行相应的操作。代码示例:

<template>
  <div class="content">
    <img
      v-for="(item, index) in arr"
      :key="index"
      src="https://bpic.51yuansu.com/pic/cover/00/19/12/57b66d41d79ad_610.jpg?x-oss-process=image/sharpen,100"
      alt="网图"
      data-src="https://img95.699pic.com/photo/50044/9004.jpg_wh300.jpg"
    />
  </div>
</template>
<script lang="ts">
import {
  defineComponent,
  Directive,
  isMemoSame,
  onMounted,
  reactive,
} from "@vue/runtime-core";
export default defineComponent({
  setup() {
    const arr = new Array(30);
    onMounted(() => {
      const imgs = document.querySelectorAll("img[data-src]");
      const config = {
        rootMargin: "0px",
        threshold: 1,
      };
      let observer = new IntersectionObserver((entries, self) => {
        entries.forEach((entry) => {
          if (entry.isIntersecting) {
            let img: any = entry.target;
            let src = img.dataset.src;
            if (src) {
              img.src = src;
              //console.log(src)
              img.removeAttribute("data-src");
            }
            // 解除观察
            self.unobserve(entry.target);
          }
        });
      }, config);

      imgs.forEach((image) => {
        observer.observe(image);
      });
    });
    return {
      arr,
    };
  },
});
</script>


运行完之后如图所示,当底部模糊的图片全部暴露到视野中,就会显示成为上面蓝色图片。

Chrome 浏览器自带

浏览器地址输入chrome://flags/#enable-lazy-image-loading

设置成Enabled,代码中使用如下:

<img src="https://source.unsplash.com/random/600" alt="" lazyload="on">

AwesomeImage

AwesomeImage 是一个支持 懒加载 / 渐进加载 / 响应加载 / 自动webp、兼容 vue2 / vue3 / nuxt 的通用图片组件
使用文档地址如下AwesomeImage
安装方式

yarn add @awesome-image/image
//或者
npm install @awesome-image/image

vue中引用如下

import AsImage from '@awesome-image/image'
import '@awesome-image/image/dist/style.css'
createApp(App).use(store).use(AsImage,{
    responsive: true,//是否是响应
    progressive: true,//是否渐进加载
}).use(router).mount('#app')

在main.ts中引用可能遇到报错,报错内容时找不到对应的生命,如图所示:
image.png
那我们我们可以声明一个declare module在.d.ts文件中

declare module '@awesome-image/image';

基本使用方式

  • 设置图片尺寸
  • 设置图片样式
  • 可添加 loading 插槽作为加载前占位元素
<style>
  .demoimage {
    width: 100%;
  }
  .loading {
    width: 100%;
    height: 100%;
    background: #eee;
  }
</style>
<template>
  <AsImage
    class="demoimage"
    :width="1280"
    :height="640"
    src="//cdn.com/image.jpg"
  >
    <template #loading>
      <div class="loading" />
    </template>
  </AsImage>
</template>

懒加载

懒加载使用 IntersectionObserver 在图片进入可视区域时加载图片。上面我们也有单独提到。

  • 使用 lazy (默认false) 属性控制是否启用懒加载, 使用 imageLazyOffset (默认0px) 设置提前多少像素加载图片
  • 如果同时使用渐进加载,使用 placeholderLazyOffset (默认0px) 设置提前多少像素加载缩略图片
<style>
...
</style>
<template>
  <AsImage
    class="demoimage"
    :width="1280"
    :height="640"
    :lazy="true"
    :imageLazyOffset="1000px"
    src="//cdn.com/image.jpg"
  >
    <template #loading>
      <div class="loading" />
    </template>
  </AsImage>
</template>

渐进加载

渐进加载使用一张宽度为 48px 的缩率图(需提前生成或搭配图片服务动态生成)作为 placeholder, 并在加载 placeholder 和 原图时通过渐显动画平滑显示。支持在 SSR 模式下,客户端注水之前渐进加载图片。

  • 需配置 imageUrlGenerator 详见 获取对应 48px 缩率图
  • 使用 progressive (默认false) 属性控制是否启用渐进加载
  • 设置 duration (默认1, 单位秒)属性控制渐显动画时长
<template>
  <AsImage
    class="demoimage"
    :width="1280"
    :height="640"
    :progressive="true"
    src="//cdn.com/image.jpg"
  >
    <template #loading>
      <div class="loading" />
    </template>
  </AsImage>
</template>

响应加载

响应加载根据屏幕宽度加载不同尺寸的图片(需提前生成或搭配图片服务动态生成)

  • 需配置 imageUrlGenerator 详见 获取不同尺寸图片url
  • 使用 responsive (默认false) 属性控制是否启用响应加载
  • 使用 breakpoints (默认[640, 768, 1024, 1280, 1536]) 属性控制可选的图片宽度
  • 使用 sizes (默认100vw) 属性控制如何选择图片宽度
简单示例
<template>
  <AsImage
    class="demoimage"
    :width="1536"
    :height="640"
    :responsive="true"
    src="//cdn.com/image.jpg"
  >
    <template #loading>
      <div class="loading" />
    </template>
  </AsImage>
</template>

根据屏幕宽度加载 640, 768, 1024, 1280, 1536 尺寸图片,如:屏幕宽度为 1000px 时加载宽度为 1024px 图片

复杂示例
<template>
  <AsImage
    class="demoimage"
    :width="1440"
    :height="640"
    :responsive="true"
    :sizes="(max-width: 640px) 100vw, (max-width: 1200px) 1000px, 50vw"
    :breakpoints="[640, 1024, 1440]"
    src="//cdn.com/image.jpg"
  >
    <template #loading>
      <div class="loading" />
    </template>
  </AsImage>
</template>

如上使用表示:

  • 屏幕宽度小于 640px 时按 100% 宽度选择图片,对应 breakpoints,则加载宽度为 640px 图片
  • 屏幕宽度小于 1200px 时按 1000px 宽度选择图片,对应 breakpoints,则加载宽度为 1024px 图片
  • 其余情况按 50% 宽度选择图片(实际使用会搭配css设置宽度为50%),对应 breakpoints,则:
    • 屏幕宽度小于 1280px 加载宽度为 640px 图片
    • 屏幕宽度小于 2048px 加载宽度为 1024px 图片
    • 屏幕宽度小于 2880px 加载宽度为 1440px 图片

加载WebP

有些图片服务商或自研服务提供了自动加载WebP格式图片的功能,对于不支持自动兼容的情况,可以指定 auto-webp 为 true (默认 false) ,来添加兼容WebP格式的功能,对于兼容的浏览器,自动加载webp图片。

<template>
  <AsImage
    class="demoimage"
    :width="1280"
    :height="640"
    :auto-webp="true"
    src="//cdn.com/image.jpg"
  >
    <template #loading>
      <div class="loading" />
    </template>
  </AsImage>
</template>

如上会加载 //cdn.com/image.jpg?format=webp (具体url根据 imageUrlGenerator 详见)

其他图片处理参数

  • quanlity 指定图片质量
  • format 指定图片格式
<template>
  <AsImage
    class="demoimage"
    :width="1280"
    :height="640"
    :quanlity="90"
    :format="png"
    src="//cdn.com/image.jpg"
  >
    <template #loading>
      <div class="loading" />
    </template>
  </AsImage>
</template>

如上会加载 //cdn.com/image.jpg?quanlity=90&format=png (具体url根据 imageUrlGenerator 详见)

awesomeImage小结

该插件对图片的加载方式比较多,可以适应我们项目中对图片的处理。它还有对wenGL的加载,有兴趣的同学可以去它的官网查看学习。

渐进式图片显示加载

vue采用progressive-image插件显示

npm install progressive-image --save

在main.js中引用

import progressive from 'progressive-image/dist/vue'; // 渐进式
import 'progressive-image/dist/index.css';//样式
 Vue.use(progressive, {
   	removePreview: true,
   	scale: true
 })

页面代码如下

<template>
    <div class="progressive">
      <img class="preview" v-progressive="http://7xiblh.com1.z0.glb.clouddn.com/progressive/2.jpg"  src="http://7xiblh.com1.z0.glb.clouddn.com/progressive/r2.jpg" />
    </div>
 </template>

响应式加载srcSet

根据我们当前屏幕的大小进行相应图片加载,这样减少资源的浪费,代码示例如下:

<img src="4.jpg" srcset="3.jpg 229w,2.png 618w,1.jpg 1000w", sizes="300px">

根据当前所设置图片大小显示对应的图片,图上代码。设置大小为300px,那么会显示2.png图片,如果sizes改为1200px,那么会显示1.jpg。srcset的顺序不会影响最终显示结果。

背景图懒加载方式

vue-lazyLoad

vue中使用的插件时vue-lazyLoad,使用的方式如下

// npm or cnpm
(c)npm install vue-lazyLoad --save-dev
// yarn
yarn add vue-lazyLoad -D

在main.ts中引用如下

import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import VueLazyLoad from "vue-lazyload"
createApp(App).use(store).use(VueLazyLoad,{
    preLoad:1,//如果该参数没有就是初始全部加载 范围时0-1 是距离顶部距离多少进行加载
}).use(router).mount('#app')

代码中使用示例如下

//html
<div class="boximag" v-lazy:background-image="require('../assets/img/1.jpeg')"></div>
//css
.boximag{
    width: 100%;
    height: 300px;
    border: 1px solid #ccc;
    background-repeat: no-repeat;
    background-size: cover;
}

以上代码是背景的懒加载,如果是图片的懒加载 ,代码如下

<img v-lazy="require('../assets/img/1.jpeg')">

通过使用v-lazy 代替了v-bind:src,也会出现滚动到相应位置进行加载效果
image.png
如果在main.ts中参数preLoad设置成0.5,页面图片效果如上图,第一张图片正常显示,第二张与第三张图片没有到达当前页面中间位置,显示为空。

vue3-lazy

还有一个vue懒加载插件时vue3-lazy

// npm or cnpm
npm install vue3-lazy -S
// yarn
yarn add vue3-lazy

代码引用如下

import lazyPlugin from 'vue3-lazy'

createApp(App)
  .use(lazyPlugin, {
    loading: require('@/assets/images/default.png'), // 图片加载时默认图片
    error: require('@/assets/images/error.png')// 图片加载失败时默认图片
  })
  .mount('#app')

使用代码示例

<img v-lazy="require('../assets/img/1.jpeg')">

它与vue-lazyLoad其中图片标签使用方式是一样的,但是缺少了背景图片的懒加载方式。大家可以根据自己使用场景对应使用。

总结

      前端图片加载的方式有很多,应用场景也会不一样。图片加载过程也会存在很多的问题,为了让我们的网站使用更加舒服,我们就需要对其不断的进行优化,这个也是我们前端必须要学会的,我上面的内容可能还有很多没有总结出来,如果大家有更好的方式可以告诉我,让我们一起学习成长。

参考资料

https://github.com/hilongjw/vue-lazyload
https://github.com/ustbhuangyi/vue3-lazy
https://awesome-image.vercel.app/
https://zhuanlan.zhihu.com/p/323174003

  • 5
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值