前言
前端图片显示加载是网站很重要的一部分,很多网站图片占用了很大的空间,网站的性能的好坏也是给用户不同的体验,好的体验感会增加用户的“幸福感”。现在很多网站中图片往往占用了很大的资源,如果图片过大,加载效率低下,占用更多资源,加载显示不完整,使用者的观感上会不舒服。那么就让我们简单了解一下前端图片加载都会用到哪些方法。
基本使用方式
<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中引用可能遇到报错,报错内容时找不到对应的生命,如图所示:
那我们我们可以声明一个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,也会出现滚动到相应位置进行加载效果
如果在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