懒加载图片

  • 这个页面在 goodsInfo 插槽里通过 <LazyImage> 渲染商品缩略图,具体位于 index.vue ,只有 row.goods?.thumbUrl 有值时才会走 <LazyImage>,否则展示一个固定大小的 "预览" 占位块;为了维持尺寸,缩略图宽高都由 goodsThumbSize 计算后传入 width/height 属性。
  • LazyImage 组件位于 LazyImage.vue (line 1),内部用 wrapperRef 挂在到外层 <div> 上,并通过 IntersectionObserver(rootMargin: '120px 0px')监听元素是否进入视窗;一旦 entry.isIntersecting,调用 loadImage 把 shouldLoad 设为 true 并断开 observer,确保图片只加载一次。
  • 模板里 shouldLoad 为 true 时才渲染 ant-design-vue 的 <Image>,并通过 imageAttrs、alt、width、height、src 把 props/attrs 透传进去;未加载前会渲染带灰背景的 .lazy-image-placeholder 占位层,避免布局抖动。
  • resolvedPreview 计算属性在 shouldLoad 变 true 以后才起作用,用来把 previewSrc、preview 相关配置组装成 Ant Design Image 可识别的预览配置,若 props.preview 为 false 则禁用预览;wrapperStyle 用来将传入的宽高参数转成 CSS 字符串保证容器尺寸。
  • 组件还支持透传其它属性 (useAttrs 去除 class/style 后的属性会直接传给 <Image>),并在 onBeforeUnmount 里清理 IntersectionObserver,保证组件卸载时不会留下监听器。

<script lang="ts" setup>

import { Image } from 'ant-design-vue';

import {

computed,

onBeforeUnmount,

onMounted,

ref,

useAttrs,

} from 'vue';

defineOptions({

name: 'LazyImage',

inheritAttrs: false,

});

const props = defineProps({

src: { type: String, default: '' },

previewSrc: { type: String, default: '' },

alt: { type: String, default: '' },

width: { type: Number, String, default: undefined },

height: { type: Number, String, default: undefined },

preview: { type: Boolean, Object, default: true },

});

const attrs = useAttrs();

const wrapperRef = ref<HTMLElement | null>(null);

const shouldLoad = ref(false);

let observer: null | IntersectionObserver = null;

const imageAttrs = computed(() => {

const { class: _class, style: _style, ...rest } = attrs;

return rest;

});

const wrapperStyle = computed(() => {

const style: Record<string, string> = {};

if (props.width !== undefined) {

style.width =

typeof props.width === 'number'

? `${props.width}px`

: String(props.width);

}

if (props.height !== undefined) {

style.height =

typeof props.height === 'number'

? `${props.height}px`

: String(props.height);

}

return style;

});

const resolvedPreview = computed(() => {

if (!shouldLoad.value) return false;

if (props.preview === false) return false;

const source = props.previewSrc || props.src;

if (props.preview === true) {

return source ? { src: source } : true;

}

const base =

props.preview && typeof props.preview === 'object' ? props.preview : {};

return { src: source, ...(base as Record<string, unknown>) };

});

function loadImage() {

if (shouldLoad.value) return;

shouldLoad.value = true;

if (observer) {

observer.disconnect();

observer = null;

}

}

onMounted(() => {

const el = wrapperRef.value;

if (!el) {

shouldLoad.value = true;

return;

}

if (typeof IntersectionObserver === 'undefined') {

shouldLoad.value = true;

return;

}

observer = new IntersectionObserver(

(entries) => {

if (entries.some((entry) => entry.isIntersecting)) {

loadImage();

}

},

{ rootMargin: '120px 0px' },

);

observer.observe(el);

});

onBeforeUnmount(() => {

if (observer) {

observer.disconnect();

observer = null;

}

});

</script>

<template>

<div

ref="wrapperRef"

:class="attrs.class"

:style="wrapperStyle, attrs.style"

>

<Image

v-if="shouldLoad"

v-bind="imageAttrs"

:src="src"

:preview="resolvedPreview"

:alt="alt"

:width="width"

:height="height"

/>

<div v-else class="lazy-image-placeholder" />

</div>

</template>

<style scoped>

.lazy-image-placeholder {

width: 100%;

height: 100%;

background: #f5f5f5;

}

</style>

相关推荐
星光不问赶路人几秒前
Vue 3 + TypeScript 中 enum 的使用实践
前端·vue.js
鸥梨菌Honevid2 分钟前
UVM 中 REQ 和 RSP 类型详解:从通信模型到 item_done 与 put_response
java·前端·servlet
IT_陈寒4 分钟前
Python多进程共享变量这个坑,差点让我加班到凌晨
前端·人工智能·后端
鸿蒙开发10 分钟前
PDF 盖黑框不等于脱敏:用 PDF.js + Canvas + pdf-lib 实现永久遮盖
前端·react.js
xixingzhe220 分钟前
Nginx 看懂请求耗时瓶颈
服务器·前端·nginx
Lank_M20 分钟前
前端AVIF编码方案评审,九个想当然逐个实测
前端
Highcharts.js27 分钟前
AI图表开发工具Highcharts Assist|自然语言生成图表代码
javascript·人工智能·highcharts·开发神器
程序员鱼皮28 分钟前
我用 REA 逆向了付费颜色 APP,结果很意外!
前端
志尊宝43 分钟前
Vue3 零基础每日笔记(087):Vue3 高频面试题(下)
前端·javascript·vue.js·笔记·ecmascript·#vue.js
pusheng20251 小时前
普晟传感再获“消防产业核心配套优质品牌”:以可靠传感技术,让全球合规变简单
大数据·前端·人工智能·物联网·安全