部分组件的拆分以及优化-Image优化
上篇记录了dialog
组件在不同业务场景下的使用,今天主要记录一下image
图片在不同场景的加载优化问题;
- 懒加载,视口加载
- 渐进式加载
- 骨架屏加载
- 异步加载
懒加载,视口加载
<!--- 懒加载,只需要添加 loading="lazy" 属性即可 -->
<img
:src="`https://cdn.wangxiaoze.cn/images/070A2001.JPG`"
alt=""
loading="lazy"
/>
<script setup lang="ts">
import { onMounted, useTemplateRef } from "vue";
const lazyRef = useTemplateRef<HTMLImageElement>("lazy-img");
// 视口加载
const onObserver = () => {
const observer = new IntersectionObserver(
entries => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target as HTMLImageElement;
img.src = img.dataset.src!;
observer.unobserve(img);
}
});
},
{
rootMargin: "0px",
threshold: 0.1,
}
);
if (lazyRef.value) {
observer.observe(lazyRef.value);
}
};
onMounted(onObserver);
</script>
<template>
<img
ref="lazy-img"
data-src="`https://cdn.wangxiaoze.cn/images/070A2001.JPG`"
alt=""
/>
</template>
<style lang="scss" scoped>
img {
width: 100%;
}
</style>
渐进式加载
<script setup lang="ts">
import { onMounted, useTemplateRef } from "vue";
const lazyRef = useTemplateRef<HTMLImageElement>("lazy-img");
const onLoad = () => {
const loadImages = (image: HTMLImageElement) => {
image.setAttribute("src", image.dataset.src!);
image.addEventListener("load", () => {
delete image.dataset.src;
});
};
if (!lazyRef.value) return;
loadImages(lazyRef.value);
};
onMounted(onLoad);
</script>
<template>
<!--
1. 配合 css 实现渐进式加载,先模糊加载 逐渐清晰
2. 没有css过渡效果,那么可实现占位图加载,先加载占位图,等到图片加载完成在加载真实图片
-->
<img
:data-src="`https://cdn.wangxiaoze.cn/images/070A2001.JPG`"
alt=""
ref="lazy-img"
/>
</template>
<style lang="scss" scoped>
img {
width: 100%;
height: 100%;
object-fit: cover;
filter: blur(0em);
transition: filter 0.5s;
&[data-src] {
filter: blur(0.2em);
}
}
</style>
骨架屏加载
<script setup lang="ts">
import { onMounted, ref, useTemplateRef } from "vue";
const divRef = useTemplateRef<HTMLDivElement>("div-ref");
const lazyRef = useTemplateRef<HTMLImageElement>("lazy-ref");
const onLoad = () => {
const loadImages = (image: HTMLImageElement) => {
image.addEventListener("load", () => {
divRef.value?.classList.remove("image-skeleton");
lazyRef.value!.style.opacity = "1";
});
};
if (!lazyRef.value) return;
loadImages(lazyRef.value);
};
onMounted(onLoad);
</script>
<template>
<div class="image-default image-skeleton" ref="div-ref">
<img
:src="`https://cdn.wangxiaoze.cn/images/070A2001.JPG`"
alt=""
ref="lazy-ref"
/>
</div>
</template>
<style lang="scss" scoped>
.image-default {
width: 100%;
&.image-skeleton {
width: 100%;
height: 400px;
background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
background-size: 200% 100%;
animation: loading 1.5s infinite;
}
img {
width: 100%;
height: 100%;
opacity: 0;
}
}
@keyframes loading {
0% {
background-position: 0 0;
}
100% {
background-position: -200% 0;
}
}
</style>
异步加载
<script setup lang="ts">
import { onMounted, useTemplateRef } from "vue";
const lazyRef = useTemplateRef<HTMLImageElement>("lazy-ref");
function loadImage(src: string) {
return new Promise(resolve => {
const img = new Image();
img.onload = () => resolve(img);
img.src = src;
lazyRef.value!.src = src;
});
}
onMounted(() => {
loadImage(`https://cdn.wangxiaoze.cn/images/070A2001.JPG`);
});
</script>
<template>
<div class="image-container">
<img src="" alt="" ref="lazy-ref" />
</div>
</template>
<style lang="scss" scoped>
.image-container {
width: 100%;
min-height: 500px;
img {
width: 100%;
}
}
</style>