Vue使用<Suspense/>实现图片加载组件

 更新时间:2026年01月25日 10:44:37   作者:咸鱼翻身更入味  
Suspense是Vue的内置组件,用于管理异步组件的加载状态,包括等待、出错和最终渲染,它通过两个插槽(default和fallback)来实现这一功能,感兴趣的可以了解一下

什么是Suspense?

SuspenseVue的内置组件,能让你管理组件加载时的等待、出错和最终渲染逻辑。

主要配合异步组件async/await 版的 setup() 使用。

它提供两个插槽:

  • default(默认插槽):要渲染的异步内容
  • fallback:等待异步内容加载时显示的兜底内容

话不多说,上代码:

第一步,实现图片异步组件

<template>
    <img 
    :src="props.src" 
    :alt="props.alt" 
    :style="{ 
      width: setWidth, 
      height: setHeight, 
      borderRadius: rounded, 
      objectFit: 'cover'
    }"
    />
</template>
// =================================== 此处为计算属性逻辑 ================================
<script setup lang="ts">
import { computed, ref } from "vue";

// 图片形状可选项
type ShapeOption = "circle" | "square" | "roundRect";

interface ILazyImage {
  src: string;
  width:string|number;
  height:string|number;
  delay?:number;//延迟执行?
  timeout?:number;//超时时间?
  shape?:ShapeOption;//图片形状
  alt?:string;
}

const props = defineProps<ILazyImage>();

//计算超时
const timeoutSet = computed(() => {
    let time = Number(props.timeout) || 5;
    return time * 1000;
});

//计算延迟
const delayTime = computed(() => {
    let time = Number(props.delay) || 0.1;
    return time * 1000;
});


//计算宽度
const setWidth = computed(() => {
    const val = parseFloat(props.width)
    if(!val) return '100px'
    return val  + "px";
});

//计算高度
const setHeight = computed(() => {
    const val = parseFloat(props.Height)
    if(!val) return '100px'
    return val  + "px";
});

// 计算圆角
const rounded = computed(() => {
    switch (props.shape) {
        case "circle":
            return "50%";
        case "square":
            return "0";
        case "roundRect":
            return "10px";
        default:
            return "10px";
    }
});

//========================实现异步加载图片==================================

const loadImage = (src: string) => {
    return new Promise((resolve, reject) => {
    // 创建一个AbortController,用于取消请求
        const controller = new AbortController();
        const signal = controller.signal;

        const timeoutId = setTimeout(() => {
        // 超时时取消请求
            controller.abort();
            reject(new Error("图片加载超时"));
        }, timeoutSet.value);

        setTimeout(() => {
            const image = new Image();
            image.src = src;
            image.onload = () => {
                clearTimeout(timeoutId);
                resolve(src);
            };
            image.onerror = () => {
                clearTimeout(timeoutId);
                reject(new Error("图片加载失败"));
            };
        }, delayTime.value);
    });
};

// 执行图片加载(async/await 触发 Suspense 等待)
await loadImage(props.src);

</script>

第二步:实现懒加载组件

  • default(默认插槽):要渲染的异步内容
  • fallback:等待异步内容加载时显示的兜底内容
  • 使用v-bind()绑定属性

<Suspense></Suspense>中直接使用异步组件即可,<template #fallback></template>则是加载时显示

<template>
    <div>
        <Suspense>
            <template #default>
                <div class="image">
                    <Image v-bind="{ ...props }"></Image>
                </div>
            </template>
            <template #fallback>
                <div class="skeleton"></div>
            </template>
        </Suspense>
    </div>
</template>

<script setup lang="ts">
import { computed } from "vue";
import Image from "./Image.vue";
// 图片形状可选项
type ShapeOption = "circle" | "square" | "roundRect";

interface ILazyImage {
  src: string;
  width:string|number;
  height:string|number;
  delay?:number;//延迟执行?
  timeout?:number;//超时时间?
  shape?:ShapeOption;//图片形状
  alt?:string;
}

const props = defineProps<ILazyImage>();

//计算超时
const timeoutSet = computed(() => {
    let time = Number(props.timeout) || 5;
    return time * 1000;
});

//计算延迟
const delayTime = computed(() => {
    let time = Number(props.delay) || 0.1;
    return time * 1000;
});


//计算宽度
const setWidth = computed(() => {
    const val = parseFloat(props.width)
    if(!val) return '100px'
    return val  + "px";
});

//计算高度
const setHeight = computed(() => {
    const val = parseFloat(props.Height)
    if(!val) return '100px'
    return val  + "px";
});

// 计算骨架屏动画:遮罩宽度(取容器宽度的 40%) 
const setSeletonWidth = computed(() => { 
    // 提取宽度数值(兼容 px 单位) 
    const widthNum = parseFloat(setWidth.value) || 100; 
    return `${widthNum * 0.4}px`;
}); 


// 骨架屏动画起始位置(从容器左侧外 30% 开始)
const setSeletonStartRight = computed(() => { 
    const widthNum = parseFloat(setWidth.value) || 100; 
    return `-${widthNum * 1.3}px`;  // 起始在容器左侧外
});

// 骨架屏动画结束位置(到容器右侧外 30%) 
const setSeletonEndRight = computed(() => { 
    const widthNum = parseFloat(setWidth.value) || 100; return `${widthNum * 1.3}px`;
    // 结束在容器右侧外
});

// 计算圆角
const rounded = computed(() => {
    switch (props.shape) {
        case "circle":
            return "50%";
        case "square":
            return "0";
        case "roundRect":
            return "10px";
        default:
            return "10px";
    }
});
</script>

<style scoped>
.image {
    overflow: hidden;
}

.skeleton {
    width: v-bind(setWidth);
    height: v-bind(setHeight);
    background-color: #dfe4ea;
    overflow: hidden;
    position: relative;

    border-radius: v-bind(rounded);
}

.skeleton::before {
    content: "";
    display: block;
    background-color: #ced6e0;
    box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.2);
    position: absolute;
    top: 0;
    right: v-bind(setSeletonStartRight);
    width: v-bind(setSeletonWidth);
    height: v-bind(setHeight);
    transform: skewX(-30deg);
    animation: ping 1s infinite ease-out;
    filter: blur(10px);
}

@keyframes ping {
    from {
        right: v-bind(setSeletonStartRight);
    }

    to {
        right: v-bind(setSeletonEndRight);
    }
}
</style>

补充

  • 代码内有些重复的内容,可使用另外的ts来声明或实现。
  • 其中可能有些变量或实现过程有误,此文章仅供参考。

到此这篇关于Vue使用&lt;Suspense/&gt;实现图片加载组件的文章就介绍到这了,更多相关Vue &lt;Suspense/&gt;图片加载内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • vue3 element-plus二次封装组件系列之伸缩菜单制作

    vue3 element-plus二次封装组件系列之伸缩菜单制作

    这篇文章主要介绍了vue3 element-plus二次封装组件系列之伸缩菜单制作,是基于vue3 vite element-plus搭建的,值的注意的时候,里面的图标组件是经过处理的,结合实例代码介绍的非常详细,需要的朋友可以参考下
    2023-01-01
  • vue+axios+promise实际开发用法详解

    vue+axios+promise实际开发用法详解

    这篇文章主要介绍了vue+axios+promise实际开发用法详解,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-10-10
  • Echarts之图例legend基本配置方式

    Echarts之图例legend基本配置方式

    这篇文章主要介绍了Echarts只图例legend基本配置方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-04-04
  • 一文轻松理解Vuex

    一文轻松理解Vuex

    这篇文章主要介绍了Vuex及其使用方法,感兴趣的同学,可以参考下
    2021-04-04
  • antd多选下拉框一行展示的实现方式

    antd多选下拉框一行展示的实现方式

    这篇文章主要介绍了antd多选下拉框一行展示的实现方式,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-10-10
  • vue引用BootStrap以及引用bootStrap-vue.js问题

    vue引用BootStrap以及引用bootStrap-vue.js问题

    这篇文章主要介绍了vue引用BootStrap以及引用bootStrap-vue.js问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-10-10
  • vue2使用element-ui,el-table不显示,用npm安装方式

    vue2使用element-ui,el-table不显示,用npm安装方式

    这篇文章主要介绍了vue2使用element-ui,el-table不显示,用npm安装方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-07-07
  • Vue 中 toRefs() 和 toRef() 的使用方法

    Vue 中 toRefs() 和 toRef() 的使用方法

    在 Vue 3 中,toRefs()可以将响应式对象的属性转换为可响应的 refs,主要用于在解构响应式对象时,保持属性的响应性,这篇文章主要介绍了Vue 中 toRefs() 和 toRef() 的使用,需要的朋友可以参考下
    2025-01-01
  • vue axios数据请求get、post方法及实例详解

    vue axios数据请求get、post方法及实例详解

    axios是一个基于Promise,同时支持浏览器端和Node.js的HTTP库,常用于Ajax请求。这篇文章主要介绍了vue axios数据请求get、post方法的使用 ,需要的朋友可以参考下
    2018-09-09
  • Vue使用pdf.js和docx-preview实现docx和pdf的在线预览

    Vue使用pdf.js和docx-preview实现docx和pdf的在线预览

    这篇文章主要为大家详细介绍了在Vue中使用pdf.js和docx-preview实现docx和pdf的在线预览的相关知识,文中的示例代码讲解详细,需要的可以参考下
    2025-03-03

最新评论