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

 更新时间:2025年07月29日 08:46:45   作者:掘金安东尼  
文章剖析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() 强制 UI 先更新
  • 异步任务分段执行:引入微任务切片 / setTimeout
  • 组件级抽象:封装 Loading 组件,减少重复逻辑
  • 进阶状态管理:用 composable 管理异步状态 + loading
  • 性能优化:避免主线程重计算、引入 Web Worker

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

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

修改后的代码:

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

  await fetchHeavyData()
  isLoading.value = false
}

原理说明:

  • 设置 isLoading.value = true
  • nextTick() 让 Vue 优先完成一次 DOM 更新
  • 之后才执行 fetchHeavyData(),此时 Loading 已渲染

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

假设 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 卡顿不只是代码问题,也涉及架构选择:

  • 是否需要提前懒加载?
  • 是否计算可以下放?
  • 是否应该服务端渲染一部分结果?

实践建议:

  • 重计算逻辑放入 worker / 后端处理
  • UI 组件中只保留显示逻辑
  • 异步函数通过组合式进行抽象封装
  • 大模型调用尽量做“流式返回”,边处理边显示

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

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

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

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

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

相关文章

  • uni-app资源引用的方法汇总(绝对路径和相对路径)

    uni-app资源引用的方法汇总(绝对路径和相对路径)

    在基于UniApp开发跨平台应用时,开发者通过特定语法来管理和加载应用程序的各种资源文件,如图片、字体、音频、视频等,在uni-app中,资源通常会被组织在项目的各个目录下,本文给大家介绍了uni-app资源引用的方法汇总,需要的朋友可以参考下
    2025-01-01
  • vue单向以及双向数据绑定方式(v-bind和v-model的使用)

    vue单向以及双向数据绑定方式(v-bind和v-model的使用)

    这篇文章主要介绍了vue单向以及双向数据绑定方式(v-bind和v-model的使用),具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-04-04
  • vue项目如何去掉URL中#符号的方法

    vue项目如何去掉URL中#符号的方法

    在开发过程中发现路径中带有/#/的标示,而且还去不掉,很丑陋,下面这篇文章主要给大家介绍了vue项目如何去掉URL中#符号的相关资料,文中通过实例代码的非常详细,需要的朋友可以参考下
    2022-07-07
  • 使用Element实现表格表头添加搜索图标和功能

    使用Element实现表格表头添加搜索图标和功能

    这篇文章主要介绍了使用Element实现表格表头添加搜索图标和功能,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-07-07
  • vue中计算属性computed和普通属性method的区别小结

    vue中计算属性computed和普通属性method的区别小结

    Vue.js中Computed和Methods是两种常用的数据处理方式,本文主要介绍了vue中计算属性computed和普通属性method的区别小结,具有一定的参考价值,感兴趣的可以了解一下
    2024-06-06
  • vue中引入mousewheel事件及兼容性处理方式

    vue中引入mousewheel事件及兼容性处理方式

    这篇文章主要介绍了vue中引入mousewheel事件及兼容性处理方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-03-03
  • vue-iview动态新增和删除的方法

    vue-iview动态新增和删除的方法

    这篇文章主要为大家详细介绍了vue-iview动态新增和删除的方法,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-06-06
  • vue-i18n使用$t导致的Typescript报错问题及解决

    vue-i18n使用$t导致的Typescript报错问题及解决

    在Vue3项目中使用vue-i18n v9.14.0时,$t属性可能因类型未声明导致TS报错,解决方案是创建src/vue-i18n.d.ts文件,添加至tsconfig.json的include项中,声明$t属性类型
    2025-08-08
  • VUE使用ElementUI下拉框 @change事件数据不回显问题

    VUE使用ElementUI下拉框 @change事件数据不回显问题

    这篇文章主要介绍了VUE使用ElementUI下拉框 @change事件数据不回显问题。具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-02-02
  • 一文详解Vue中加上key后发生什么

    一文详解Vue中加上key后发生什么

    本文主要介绍了一文详解Vue中加上key后发生什么,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-04-04

最新评论