资讯动态

Vue3实战:5种优雅的Loading动画实现方案(附完整代码)

发布时间:2026/8/15 4:10:28 来源:尧图企业网站定制
Vue3实战5种优雅的Loading动画实现方案附完整代码在单页应用开发中Loading动画不仅是缓解用户等待焦虑的视觉缓冲更是提升产品专业度的细节体现。Vue3凭借其组合式API和内置组件为开发者提供了多种实现Loading效果的优雅方案。本文将深入探讨五种典型场景下的实现技巧涵盖从基础到进阶的完整代码示例。1. 首次加载全局Loading当应用初始化时Vue实例需要时间挂载到DOM。传统白屏体验可以通过简单的HTML占位符优化!-- public/index.html -- div idapp div classglobal-loading svg classspinner viewBox0 0 50 50 circle cx25 cy25 r20 fillnone stroke-width5/circle /svg p资源加载中.../p /div /div配合CSS动画增强视觉效果/* main.css */ .global-loading { display: flex; flex-direction: column; align-items: center; justify-content: center; height: 100vh; } .spinner { animation: rotate 2s linear infinite; width: 50px; height: 50px; } .spinner circle { stroke: #3498db; stroke-linecap: round; animation: dash 1.5s ease-in-out infinite; } keyframes rotate { 100% { transform: rotate(360deg); } } keyframes dash { 0% { stroke-dasharray: 1,150; stroke-dashoffset: 0; } 50% { stroke-dasharray: 90,150; stroke-dashoffset: -35; } 100% { stroke-dasharray: 90,150; stroke-dashoffset: -124; } }提示这种方案无需JavaScript控制当Vue完成挂载后会自动替换占位内容。适合作为基础保底方案。2. 路由切换过渡动画利用Vue Router的v-slot和Transition组件实现路由级Loading!-- App.vue -- template router-view v-slot{ Component, route } transition :nameroute.meta.transition || fade suspense template #default component :isComponent :keyroute.path / /template template #fallback div classroute-loading div classprogress-bar / /div /template /suspense /transition /router-view /template style scoped .route-loading { position: fixed; top: 0; left: 0; width: 100%; height: 3px; background: transparent; } .progress-bar { height: 100%; width: 0; background: linear-gradient(90deg, #ff4d4d, #f9cb28); animation: progress 2s ease-in-out infinite; } keyframes progress { 0% { width: 0; left: 0; } 50% { width: 100%; left: 0; } 100% { width: 0; left: 100%; } } /style关键实现要点结合Suspense处理异步组件加载通过路由元信息配置不同过渡效果进度条动画使用CSS实现避免性能开销3. 异步组件加载状态对于代码分割的异步组件Vue3的defineAsyncComponent提供了更灵活的定义方式// utils/asyncComponents.js import { defineAsyncComponent } from vue export const AsyncModal defineAsyncComponent({ loader: () import(./ModalComponent.vue), loadingComponent: { template: div classasync-loading div classdot-flashing / /div , setup() { // 可添加加载状态逻辑 } }, delay: 200, // 延迟显示loading时间 timeout: 3000, // 超时时间 errorComponent: { template: div加载失败/div }, suspensible: false // 是否由Suspense控制 })配套的点阵闪烁动画样式.dot-flashing { position: relative; width: 10px; height: 10px; border-radius: 5px; background-color: #9880ff; color: #9880ff; animation: dotFlashing 1s infinite alternate; } .dot-flashing::before, .dot-flashing::after { content: ; display: inline-block; position: absolute; top: 0; } .dot-flashing::before { left: -15px; width: 10px; height: 10px; border-radius: 5px; background-color: #9880ff; color: #9880ff; animation: dotFlashing 1s infinite alternate; animation-delay: 0.3s; } .dot-flashing::after { left: 15px; width: 10px; height: 10px; border-radius: 5px; background-color: #9880ff; color: #9880ff; animation: dotFlashing 1s infinite alternate; animation-delay: 0.6s; } keyframes dotFlashing { 0% { opacity: 0.5; transform: scale(0.8); } 100% { opacity: 1; transform: scale(1.2); } }4. 数据请求Loading封装对于API请求场景可以创建高阶Hook统一管理Loading状态// hooks/useLoading.ts import { ref } from vue export function useLoadingT extends (...args: any[]) Promiseany(fn: T) { const isLoading ref(false) const error refError | null(null) const execute async (...args: ParametersT): PromiseReturnTypeT | null { isLoading.value true error.value null try { return await fn(...args) } catch (err) { error.value err as Error return null } finally { isLoading.value false } } return { isLoading, error, execute } }组件中的实际应用示例script setup langts import { useLoading } from ./hooks/useLoading import { fetchUserData } from ./api const { isLoading: isUserLoading, execute: loadUser, error: userError } useLoading(fetchUserData) const loadData async () { const data await loadUser(user123) if (data) { // 处理数据 } } /script template button clickloadData :disabledisUserLoading template v-if!isUserLoading加载数据/template template v-else span classbutton-loader/span 加载中... /template /button /template style .button-loader { display: inline-block; width: 12px; height: 12px; border: 2px solid rgba(255,255,255,0.3); border-radius: 50%; border-top-color: #fff; animation: spin 1s ease-in-out infinite; margin-right: 8px; } keyframes spin { to { transform: rotate(360deg); } } /style5. 骨架屏高级实现骨架屏(Skeleton Screen)作为现代Loading的最佳实践可以通过动态组件实现!-- components/SkeletonLoader.vue -- script setup langts defineProps{ type?: card | list | profile count?: number }() /script template div classskeleton-container template v-iftype card div v-fori in count :keyi classskeleton-card div classskeleton-image/div div classskeleton-line w-75/div div classskeleton-line w-50/div /div /template template v-else-iftype list div v-fori in count :keyi classskeleton-list-item div classskeleton-avatar/div div classskeleton-content div classskeleton-line/div div classskeleton-line w-80/div /div /div /template template v-else div classskeleton-profile div classskeleton-avatar-lg/div div classskeleton-line w-60/div div classskeleton-line w-40/div /div /template /div /template style scoped .skeleton-container { --skeleton-color: #f0f0f0; --skeleton-highlight: #e0e0e0; } .skeleton-card, .skeleton-list-item, .skeleton-profile { background: white; border-radius: 8px; margin-bottom: 16px; } .skeleton-image { height: 120px; background: var(--skeleton-color); border-radius: 4px 4px 0 0; position: relative; overflow: hidden; } .skeleton-avatar { width: 40px; height: 40px; border-radius: 50%; background: var(--skeleton-color); } .skeleton-avatar-lg { width: 80px; height: 80px; border-radius: 50%; background: var(--skeleton-color); margin: 0 auto 16px; } .skeleton-line { height: 12px; background: var(--skeleton-color); border-radius: 4px; margin: 8px 0; position: relative; overflow: hidden; } .w-40 { width: 40%; } .w-50 { width: 50%; } .w-60 { width: 60%; } .w-75 { width: 75%; } .w-80 { width: 80%; } /* 骨架屏动画 */ .skeleton-image::after, .skeleton-line::after, .skeleton-avatar::after, .skeleton-avatar-lg::after { content: ; position: absolute; top: 0; left: -100%; width: 100%; height: 100%; background: linear-gradient( 90deg, transparent, var(--skeleton-highlight), transparent ); animation: shimmer 1.5s infinite; } keyframes shimmer { 100% { left: 100%; } } /style在组件中的智能切换使用script setup import { ref } from vue import SkeletonLoader from ./SkeletonLoader.vue const products ref([]) const isLoading ref(true) fetchProducts().then(data { products.value data isLoading.value false }) /script template div classproduct-list template v-ifisLoading SkeletonLoader typecard :count4 / /template template v-else ProductCard v-forproduct in products :keyproduct.id :productproduct / /template /div /template实现骨架屏的关键技术点布局匹配确保骨架元素与实际内容布局一致动画优化使用CSS渐变实现高性能的shimmer效果类型系统通过props支持多种内容类型的骨架响应式设计适配不同屏幕尺寸的骨架展示

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价