前言
在开发的过程中,为了页面价加载速度快、用户体验好我们常常会对图片进行预加载和懒加载。对于这两种什么时候用,具体要看情况而定。
预加载
什么是预加载
提前加载图片,加载完毕后会缓存到本地
,当用户需要查看时可直接从本地缓存中渲染,拥有良好的用户体验。
为什么要使用预加载?
用户更好的体验,减少等待的时间
。这种做法实际上牺牲了服务器的性能换取了更好的用户体验
。
使用举例
像是专门的图片网站,为了第一次打开的用户体验(不长时间的白屏,没有耐心的用户会关闭网页),就会使用预加载,但也不是全部加载就预加载一部分,当你在网站中浏览图片时,这时候又悄悄的加载一部分图片,这样就可以达到图片无缝连接的效果,你丝毫不会感觉慢。
还有像是浏览一组图片时,只给你显示第一张图片,然后可以通过一些形式(点击按钮)来切换图片,在你浏览第一张图片时,其实一整组图片都预加载完了,就等你切换了。
代码实现预加载
使用预加载前
可以明显看出在点击按钮时有一段的延时,然后再切换图片,这就是在请求图片(用户体验不行),再看图确实是从服务器请求来的,没有使用缓存。
使用预加载
可以看到点击切换按钮后,页面迅速做出反应,无缝连接(用户体验极好),再看图片确实是提请请求,使用本地缓存。等到需要的时候,命中缓存,然后直接从缓存中渲染。
使用JS来实现
<template>
<div>
<img :src="imgData[imgInd]" alt="" />
</div>
<button @click="onLeft">Left</button>
<button @click="onRight">Right</button>
</template>
<script lang="ts">
import { ref } from "vue";
export default {
name: "App",
setup() {
let imgData = ref<string[]>([
"https://cdn.jsdelivr.net/gh/lztnb/img@master/3.jpg",
"https://cdn.jsdelivr.net/gh/lztnb/img@master/4.jpg",
"https://cdn.jsdelivr.net/gh/lztnb/img@master/5.jpg",
"https://cdn.jsdelivr.net/gh/lztnb/img@master/7.jpg",
]);
// 预加载图片
function preloadImg(srcArr: string[]): void {
if (srcArr instanceof Array) {
for (var i = 0; i < srcArr.length; i++) {
var oImg = new Image();
oImg.src = srcArr[i];
}
}
}
preloadImg(imgData.value);
let imgInd = ref<number>(0);
function onLeft() {
imgInd.value - 1 < 0 ? 0 : --imgInd.value;
}
function onRight() {
imgInd.value + 1 == imgData.value.length
? imgData.value.length - 1
: ++imgInd.value;
}
return {
onLeft,
onRight,
imgInd,
imgData,
};
},
};
</script>
CSS实现
<div>
<img :src="imgData[imgInd]" alt="" />
</div>
<button @click="onLeft">Left</button>
<button @click="onRight">Right</button>
<div class="img1"></div>
<div class="img2"></div>
<div class="img3"></div>
<div class="img4"></div>
</template>
<script lang="ts">
import { ref } from "vue";
export default {
name: "App",
setup() {
let imgData = ref<string[]>([
"https://cdn.jsdelivr.net/gh/lztnb/img@master/3.jpg",
"https://cdn.jsdelivr.net/gh/lztnb/img@master/4.jpg",
"https://cdn.jsdelivr.net/gh/lztnb/img@master/5.jpg",
"https://cdn.jsdelivr.net/gh/lztnb/img@master/7.jpg",
]);
let imgInd = ref<number>(0);
function onLeft() {
imgInd.value - 1 < 0 ? 0 : --imgInd.value;
}
function onRight() {
imgInd.value + 1 == imgData.value.length
? imgData.value.length - 1
: ++imgInd.value;
}
return {
onLeft,
onRight,
imgInd,
imgData,
};
},
};
</script>
<style scoped>
.img1 {
height: 0px;
width: 0px;
background: url(https://cdn.jsdelivr.net/gh/lztnb/img@master/3.jpg);
}
.img2 {
height: 0px;
width: 0px;
background: url(https://cdn.jsdelivr.net/gh/lztnb/img@master/4.jpg);
}
.img3 {
height: 0px;
width: 0px;
background: url(https://cdn.jsdelivr.net/gh/lztnb/img@master/5.jpg);
}
.img4 {
height: 0px;
width: 0px;
background: url(https://cdn.jsdelivr.net/gh/lztnb/img@master/5.jpg);
}
</style>
懒加载
什么是懒加载
懒加载可以说是延迟加载甚至是不加载。当访问一个页面的时候,用统一图片路径来代替(这样就请求一次,减轻服务器压力,俗称占位图
),只有当图片出现在浏览器的可视区域内时,才设置图片正真的路径,请求服务器。
为什么要使用懒加载
懒加载的主要目的是作为服务器前端的优化,减少请求数或延迟请求
。页面加载速度快、可以减轻服务器的压力、节约了流量,用户体验好
使用举例
像是商场的页面,图片数量比较多,要是一次性请求加载,没个十几秒出不来。所以先出现用户界面需要的图片,将来可能需要出现在页面上的,等将来到了再加载(预加载是不等将来,直接请求)。
代码实现图片懒加载
没有使用懒加载
使用懒加载,第一次进页面就快了不少
懒加载代码
原理:图片到最外层offsetTop距离-(图片最近滚动父元素)到最外层offsetTop距离-(图片最近滚动父元素)的scrollTop距离 <= (图片最近滚动父元素)clientHeight距离[+X] (当然可以为了更好的用户体验可以提前加载,再加上一个距离X
)。(随便你怎么嵌套滚动,都是可以的,可以在这个基础上再判断父元素是否出现在body的可视范围内。)
看不懂描述没关系,看图
<template>
<div>图片懒加载</div>
<div class="imgFrame">
<img
v-for="(data, ind) in imgData"
:key="ind"
:src="imgSrcArray[ind]"
:ref="imgDom"
/>
</div>
</template>
<script lang="ts">
import { ref } from "vue";
export default {
name: "App",
setup() {
let imgSrcArray = ref<string[]>([]);
// 要懒加载的数据
let imgData = ref<string[]>([
"https://cdn.jsdelivr.net/gh/lztnb/img@master/3.jpg",
"https://cdn.jsdelivr.net/gh/lztnb/img@master/4.jpg",
"https://cdn.jsdelivr.net/gh/lztnb/img@master/5.jpg",
"https://cdn.jsdelivr.net/gh/lztnb/img@master/6.jpg",
"https://cdn.jsdelivr.net/gh/lztnb/img@master/7.jpg",
"https://cdn.jsdelivr.net/gh/lztnb/img@master/14.jpg",
"https://cdn.jsdelivr.net/gh/lztnb/img@master/15.jpg",
"https://cdn.jsdelivr.net/gh/lztnb/img@master/16.jpg",
"https://cdn.jsdelivr.net/gh/lztnb/img@master/17.jpg",
"https://cdn.jsdelivr.net/gh/lztnb/img@master/18.jpg",
"https://cdn.jsdelivr.net/gh/lztnb/img@master/19.jpg",
"https://cdn.jsdelivr.net/gh/lztnb/img@master/20.jpg",
"https://cdn.jsdelivr.net/gh/lztnb/img@master/22.jpg",
"https://cdn.jsdelivr.net/gh/lztnb/img@master/23.jpg",
"https://cdn.jsdelivr.net/gh/lztnb/img@master/24.jpg",
"https://cdn.jsdelivr.net/gh/lztnb/img@master/25.jpg",
"https://cdn.jsdelivr.net/gh/lztnb/img@master/26.jpg",
"https://cdn.jsdelivr.net/gh/lztnb/img@master/27.jpg",
"https://cdn.jsdelivr.net/gh/lztnb/img@master/28.jpg",
"https://cdn.jsdelivr.net/gh/lztnb/img@master/29.jpg",
"https://cdn.jsdelivr.net/gh/lztnb/img@master/30.jpg",
"https://cdn.jsdelivr.net/gh/lztnb/img@master/31.jpg",
"https://cdn.jsdelivr.net/gh/lztnb/img@master/32.jpg",
"https://cdn.jsdelivr.net/gh/lztnb/img@master/33.jpg",
]);
// 使用默认图片
imgData.value.forEach((val, ind: number) => {
imgSrcArray.value[ind] =
"https://acmphoto.oss-cn-beijing.aliyuncs.com/%E5%8A%A0%E8%BD%BD%E4%B8%AD4_3.png";
});
// imgDOM的元素
let imgDomArray = ref<HTMLElement[]>([]);
const imgDom = (el: HTMLElement) => {
imgDomArray.value.push(el);
};
// 获取最近可以滚动的父元素
function getParent<T extends HTMLElement>(e: T): T {
let parentDom = e.parentNode;
// eslint-disable-next-line no-constant-condition
while (true) {
if (parentDom == document.body) {
break;
}
if ((parentDom as T).clientHeight == (parentDom as T).scrollHeight) {
parentDom = getParent(parentDom as T);
} else {
break;
}
}
return parentDom as T;
}
// 得到距离页面最上方的距离
function getoffsetTop<T extends HTMLElement>(e: T): number {
let offset = e.offsetTop;
// 一直递归到最外层
if (e.offsetParent != null) {
offset += getoffsetTop(e.offsetParent as T);
}
return offset;
}
// 节流
let time: number | null;
onScroll();
function onScroll() {
if (time == null) {
time = setTimeout(() => {
onShow();
time = null;
}, 500);
}
}
// 判断是否加载
function onShow(): void {
for (let i = 0; i < imgSrcArray.value.length; i++) {
let cHeight = 0;
let sTop = 0;
let parentDom = getParent(imgDomArray.value[i]);
parentDom.addEventListener("scroll", onScroll);
if (parentDom == document.body) {
cHeight = document.documentElement.clientHeight as number;
sTop = document.documentElement.scrollTop as number;
window.addEventListener("scroll", onScroll);
} else {
cHeight = parentDom.clientHeight;
sTop = parentDom.scrollTop;
parentDom.addEventListener("scroll", onScroll);
}
//判断是否加载的关键
if (
getoffsetTop(imgDomArray.value[i]) - getoffsetTop(parentDom) - sTop <=
cHeight
) {
imgSrcArray.value[i] = imgData.value[i];
}
}
}
return {
imgData,
imgSrcArray,
imgDom,
};
},
};
</script>
<style scoped>
.imgFrame {
margin-top: 20px;
width: 800px;
height: 600px;
overflow: auto;
}
img {
height: 100%;
width: 100%;
object-fit: contain;
}
</style>
结语
通过这样的从理论到实操,应该对图片的预加载和懒加载都有所了解。不过在开发中还是有插件就用插件,这样使开发更快更简单,不用自己写很多代码,说不定还有bug。但是合格的打工人还是要知道原理和会手撸代码,插件可以用,当原理也要会。