vue.js

关注公众号 jb51net

关闭
首页 > 网络编程 > JavaScript > javascript类库 > vue.js > Vue项目中loading卡死

Vue项目中loading卡死的问题及解决方案

作者:掘金安东尼

文章剖析Vue项目中loading卡死问题,揭示其本质为异步阻塞与响应式渲染冲突,提出分五层解决方案:nextTick强制刷新、任务分片/Worker解耦、组件封装、组合式API管理状态、性能优化与架构调整,强调需从UI、异步、计算等多维度优化

Vue 项目中常见的“loading 卡死”问题并不是简单的样式或动画问题,而往往隐藏着异步阻塞、渲染延迟、主线程拥堵、数据结构设计混乱等一系列问题。

本文将从原理讲起,结合实际案例与优化手段,逐层剖析“loading 卡死”的真相,并提供完整的代码解决方案与可复用的组件封装方法。

一、问题现象:页面卡在 Loading,用户无响应

我们先来看一个典型场景:

<template>
  <div v-if="isLoading">加载中,请稍候...</div>
  <div v-else>
    <!-- 正常内容 -->
  </div>
</template>

<script setup>
import { ref } from 'vue'

const isLoading = ref(false)

const loadData = async () => {
  isLoading.value = true
  await fetchHeavyData() // 假设这个函数运行时间较长
  isLoading.value = false
}
</script>

问题来了:当 loadData() 执行时,页面经常卡住在“空白”阶段,甚至 Loading 都没有渲染出来

这不是 Bug 吗?我们不是已经设置了 isLoading.value = true 吗?

二、问题本质:Vue 响应式渲染 + 异步执行的同步机制冲突

Vue 的响应式系统是基于虚拟 DOM + 异步批量更新机制。

当你设置:

isLoading.value = true

Vue 会将这个更新放入任务队列(如微任务),等待当前同步任务执行完成后统一处理。

但接下来你立即执行:

await fetchHeavyData()

这个函数本身也许涉及大量计算、网络延迟或阻塞逻辑,它阻止了 UI 更新队列的执行

类似于:

setState(loading=true)
heavySyncTask()
// UI 还没来得及渲染,就被卡住了

所以 loading 状态“被设置”了,但页面没有“时间”去更新它的显示。

三、解决方案路线图

我们将问题分为 5 层进行解决:

四、第一层:使用nextTick()强制触发渲染

Vue 提供了一个函数叫 nextTick(),它允许你等待 DOM 更新完毕后再继续执行。

修改后的代码:

const loadData = async () => {
  isLoading.value = true
  await nextTick() // 强制渲染 Loading

  await fetchHeavyData()
  isLoading.value = false
}

原理说明:

五、第二层:将异步任务切片执行,避免长时间阻塞

假设 fetchHeavyData() 内部有复杂的同步逻辑,比如遍历上万条数据进行预处理:

function fetchHeavyData() {
  const raw = getRawData()
  const processed = raw.map(x => compute(x)) // 耗时
  return processed
}

这种情况下,即使你用了 nextTick(),UI 也依然会卡顿。

优化策略:用分片机制

async function fetchHeavyData() {
  const raw = getRawData()
  const result = []
  for (let i = 0; i < raw.length; i++) {
    result.push(compute(raw[i]))
    if (i % 100 === 0) await new Promise(r => setTimeout(r, 0)) // 释放主线程
  }
  return result
}

或使用 Web Worker 解耦主线程

将计算逻辑放入 Worker 中,主线程专注 UI。

六、第三层:封装可复用的 Loading 组件

很多项目中,loading 状态管理分散在多个组件中,造成逻辑重复。

我们可以封装一个标准的 <AiLoading> 组件:

<template>
  <div v-if="visible" class="ai-loading">
    <svg>...</svg>
    <p>{{ text }}</p>
  </div>
</template>

<script setup>
defineProps({
  visible: Boolean,
  text: {
    type: String,
    default: '正在分析,请稍候...'
  }
})
</script>

然后这样使用:

<AiLoading :visible="isLoading" text="AI 正在写日报" />

优势:统一样式、交互友好、可控性强。

七、第四层:组合式 API 管理异步状态

通过封装 useAsyncTask() composable,我们可以更优雅地管理 loading 状态:

export function useAsyncTask(fn) {
  const isLoading = ref(false)
  const run = async (...args) => {
    isLoading.value = true
    await nextTick()
    const result = await fn(...args)
    isLoading.value = false
    return result
  }
  return { run, isLoading }
}

使用方式:

const { run: fetch, isLoading } = useAsyncTask(fetchHeavyData)
await fetch()

八、第五层:性能优化与架构思考

Vue 项目中 loading 卡顿不只是代码问题,也涉及架构选择:

实践建议:

九、总结全文:一个 loading 问题的技术全景图

层级解决方式关键词
1nextTick 强制刷新UI 渲染
2任务分片 / Worker主线程解放
3组件封装可维护性
4异步状态管理组合式 API响应式设计
5性能调优与架构优化全局优化

loading 卡顿只是表象,背后是 UI、异步、计算、结构之间的角力。

掌握这些技巧,你不仅能解决一个问题,更能理解 Vue 项目的性能瓶颈与交互优化方向。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

您可能感兴趣的文章:
阅读全文