You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

初学者咨询: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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.11 12:27:03