初学者咨询:Vue项目中API请求返回数据前如何展示Skeleton骨架屏
骨架屏实现方案
你目前想到的setTimeout方案存在明显缺陷:API请求耗时受网络环境、服务端响应速度影响波动很大,固定定时时长要么会出现请求还没回来骨架屏就消失,要么数据早就返回了还让用户无意义等待,体验很差。
核心实现逻辑
正确方案的核心是把骨架屏的显隐和API请求的生命周期绑定,不需要单独设置固定定时器:
- 首先定义两个状态:
isLoading(布尔值,默认初始化为true)、pageData(存储接口返回的数据,默认初始化为null) - 页面初始化阶段直接渲染骨架屏,判定条件为
isLoading === true或者pageData === null - 发起API请求,不管请求成功还是失败,在请求结束的回调里把
isLoading修改为false,请求成功的话同时给pageData赋值接口返回的数据 - 状态更新后,页面自动判定不再满足骨架屏显示条件,切换为正式内容渲染
不同场景的代码示例
原生JS实现示例
// 初始化状态 let isLoading = true const contentBox = document.querySelector('.content-box') // 页面初始先渲染骨架屏 contentBox.innerHTML = `<div class="skeleton">此处放置骨架屏DOM结构</div>` // 发起请求 fetch('你的业务API地址') .then(res => res.json()) .then(data => { // 存储接口返回数据 window.pageData = data }) .catch(err => { console.error('请求失败', err) }) .finally(() => { isLoading = false // 替换为正式内容 contentBox.innerHTML = `<div class="real-content">此处放置真实内容DOM结构</div>` })
Vue3实现示例
<template> <div> <!-- 加载状态为真时显示骨架屏 --> <Skeleton v-if="isLoading" /> <!-- 加载结束后显示真实内容 --> <div class="real-content" v-else> {{ pageData.title }} <!-- 其他内容渲染逻辑 --> </div> </div> </template> <script setup> import { ref, onMounted } from 'vue' const isLoading = ref(true) const pageData = ref(null) const fetchData = async () => { try { const res = await fetch('你的业务API地址') pageData.value = await res.json() } catch (err) { console.error('请求失败', err) } finally { isLoading.value = false } } onMounted(() => { fetchData() }) </script>
React实现示例
import { useState, useEffect } from 'react' import Skeleton from './Skeleton' function Page() { const [isLoading, setIsLoading] = useState(true) const [pageData, setPageData] = useState(null) const fetchData = async () => { try { const res = await fetch('你的业务API地址') const data = await res.json() setPageData(data) } catch (err) { console.error('请求失败', err) } finally { setIsLoading(false) } } useEffect(() => { fetchData() }, []) return ( <div> {isLoading ? <Skeleton /> : ( <div className="real-content"> {pageData?.title} {/* 其他内容渲染逻辑 */} </div> )} </div> ) }
特殊场景补充说明
如果想要避免网络特别好的时候API请求太快,骨架屏一闪而过的问题,可以加最小展示时长的兜底逻辑,这时候才需要和定时器结合使用,而非单独使用固定定时器:
// 示例:要求骨架屏最少展示800ms const fetchData = async () => { const startTime = Date.now() setIsLoading(true) try { const res = await fetch('你的业务API地址') const data = await res.json() setPageData(data) } catch (err) { console.error(err) } finally { const costTime = Date.now() - startTime // 计算还需要等待多久才到最小展示时长 const delay = costTime < 800 ? 800 - costTime : 0 setTimeout(() => { setIsLoading(false) }, delay) } }
内容的提问来源于stack exchange,提问作者Brefer
相关产品推荐
相关产品推荐

