Home
avatar

.Wang

部分组件的拆分以及优化-Image优化

上篇记录了dialog组件在不同业务场景下的使用,今天主要记录一下image图片在不同场景的加载优化问题;

  1. 懒加载,视口加载
  2. 渐进式加载
  3. 骨架屏加载
  4. 异步加载

懒加载,视口加载

<!--- 懒加载,只需要添加 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>
Vue