返回文章列表
别让"太快"毁掉体验:为 Loading 加一个延迟
2026年6月6日 19:3812 分钟阅读lemon

别让"太快"毁掉体验:为 Loading 加一个延迟

#前端

问题

前端项目中,几乎每个页面都有 loading 状态:发请求时显示骨架屏或转圈,请求结束后关掉。

async function loadOrders() {
  loading.value = true
  orderList.value = await fetchOrders()
  loading.value = false
}

这看起来理所当然。但实际体验中有一个被忽视的问题——当请求太快时,loading 反而是一种负体验

设想以下场景:

  • 用户点开订单列表
  • 屏幕先是正常内容
  • 瞬间闪出一个全屏转圈
  • 不到 200ms 又消失了
  • 正文出现

这种"闪一下"比什么都不显示更糟糕。人眼捕捉到了状态切换——尽管内容早就加载完了。loading 没有被感知为"等待中",而是被感知为"页面抖了一下"。

这在小程序中尤其明显,因为很多接口在良好网络下延迟只有 200-400ms。

思路

核心策略:只有请求确实慢的时候,才值得显示 loading

请求耗时 ≤ 300ms  →  不显示 loading,直接出内容
请求耗时 > 300ms  →  正常显示 loading

实现方式:不立即显示 loading,而是设一个定时器,300ms 后如果请求还没完成,才把 loading 亮出来。

如果请求在 300ms 内完成了,定时器被清除,loading 永远不会出现。

实现

useDelayedLoading 用 Vue 的 computed({ get, set }) 实现,对调用方完全透明——现有代码不需要改任何调用方式:

import { ref, computed } from 'vue'

export function useDelayedLoading(delay = 300) {
  const show = ref(false)
  let timer = null

  const loading = computed({
    get: () => show.value,
    set: (val) => {
      if (timer) {
        clearTimeout(timer)
        timer = null
      }
      if (val) {
        timer = setTimeout(() => {
          show.value = true
        }, delay)
      } else {
        show.value = false
      }
    },
  })

  return { loading }
}

使用

对现有代码的侵入性为零。只需要两处修改:

修改前:

const loading = ref(false)

修改后:

import { useDelayedLoading } from '@/utils/useDelayedLoading'

const { loading } = useDelayedLoading()

其余一切照旧——模板绑定、赋值都无需变动:

<nut-loading-page :loading="loading" />
loading.value = true
const data = await fetchData()
loading.value = false

哪些场合不该延迟

并非所有 loading 都适合延迟:

场景是否延迟原因
页面初次加载、列表刷新避免闪烁
详情/封面/媒体加载同上
提交按钮的 loading 状态防止重复点击,需立即响应
分页加载更多的 loading防止重复触发翻页
上传进度条需要即时反馈

参数

const { loading } = useDelayedLoading(400)  // 自定义 400ms
const { loading } = useDelayedLoading()      // 默认 300ms

300ms 是一个经验值——大致等于人眼感知到"卡顿"的临界点,也略短于良好网络下大多数 API 请求的响应时间。

效果

优化前,快速加载页面会经历:内容 → 空 → loading → 内容,页面"抖"一次。

优化后,快请求直接走:内容,一次渲染到底。只有真正慢的请求才会看到 loading,而此时 loading 的出现是合理的、符合预期的。

Thanks for reading.

评论

登录后即可评论

去登录
加载中...