Vue項(xiàng)目中l(wèi)oading卡死的問題及解決方案
Vue 項(xiàng)目中常見的“loading 卡死”問題并不是簡單的樣式或動畫問題,而往往隱藏著異步阻塞、渲染延遲、主線程擁堵、數(shù)據(jù)結(jié)構(gòu)設(shè)計(jì)混亂等一系列問題。
本文將從原理講起,結(jié)合實(shí)際案例與優(yōu)化手段,逐層剖析“loading 卡死”的真相,并提供完整的代碼解決方案與可復(fù)用的組件封裝方法。
一、問題現(xiàn)象:頁面卡在 Loading,用戶無響應(yīng)
我們先來看一個(gè)典型場景:
<template>
<div v-if="isLoading">加載中,請稍候...</div>
<div v-else>
<!-- 正常內(nèi)容 -->
</div>
</template>
<script setup>
import { ref } from 'vue'
const isLoading = ref(false)
const loadData = async () => {
isLoading.value = true
await fetchHeavyData() // 假設(shè)這個(gè)函數(shù)運(yùn)行時(shí)間較長
isLoading.value = false
}
</script>
問題來了:當(dāng) loadData() 執(zhí)行時(shí),頁面經(jīng)??ㄗ≡?ldquo;空白”階段,甚至 Loading 都沒有渲染出來。
這不是 Bug 嗎?我們不是已經(jīng)設(shè)置了 isLoading.value = true 嗎?
二、問題本質(zhì):Vue 響應(yīng)式渲染 + 異步執(zhí)行的同步機(jī)制沖突
Vue 的響應(yīng)式系統(tǒng)是基于虛擬 DOM + 異步批量更新機(jī)制。
當(dāng)你設(shè)置:
isLoading.value = true
Vue 會將這個(gè)更新放入任務(wù)隊(duì)列(如微任務(wù)),等待當(dāng)前同步任務(wù)執(zhí)行完成后統(tǒng)一處理。
但接下來你立即執(zhí)行:
await fetchHeavyData()
這個(gè)函數(shù)本身也許涉及大量計(jì)算、網(wǎng)絡(luò)延遲或阻塞邏輯,它阻止了 UI 更新隊(duì)列的執(zhí)行。
類似于:
setState(loading=true) heavySyncTask() // UI 還沒來得及渲染,就被卡住了
所以 loading 狀態(tài)“被設(shè)置”了,但頁面沒有“時(shí)間”去更新它的顯示。
三、解決方案路線圖
我們將問題分為 5 層進(jìn)行解決:
- 最小解決方案:使用
nextTick()強(qiáng)制 UI 先更新 - 異步任務(wù)分段執(zhí)行:引入微任務(wù)切片 / setTimeout
- 組件級抽象:封裝 Loading 組件,減少重復(fù)邏輯
- 進(jìn)階狀態(tài)管理:用 composable 管理異步狀態(tài) + loading
- 性能優(yōu)化:避免主線程重計(jì)算、引入 Web Worker
四、第一層:使用nextTick()強(qiáng)制觸發(fā)渲染
Vue 提供了一個(gè)函數(shù)叫 nextTick(),它允許你等待 DOM 更新完畢后再繼續(xù)執(zhí)行。
修改后的代碼:
const loadData = async () => {
isLoading.value = true
await nextTick() // 強(qiáng)制渲染 Loading
await fetchHeavyData()
isLoading.value = false
}
原理說明:
- 設(shè)置
isLoading.value = true nextTick()讓 Vue 優(yōu)先完成一次 DOM 更新- 之后才執(zhí)行
fetchHeavyData(),此時(shí) Loading 已渲染
五、第二層:將異步任務(wù)切片執(zhí)行,避免長時(shí)間阻塞
假設(shè) fetchHeavyData() 內(nèi)部有復(fù)雜的同步邏輯,比如遍歷上萬條數(shù)據(jù)進(jìn)行預(yù)處理:
function fetchHeavyData() {
const raw = getRawData()
const processed = raw.map(x => compute(x)) // 耗時(shí)
return processed
}
這種情況下,即使你用了 nextTick(),UI 也依然會卡頓。
優(yōu)化策略:用分片機(jī)制
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 解耦主線程
將計(jì)算邏輯放入 Worker 中,主線程專注 UI。
六、第三層:封裝可復(fù)用的 Loading 組件
很多項(xiàng)目中,loading 狀態(tài)管理分散在多個(gè)組件中,造成邏輯重復(fù)。
我們可以封裝一個(gè)標(biāo)準(zhǔn)的 <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 正在寫日報(bào)" />
優(yōu)勢:統(tǒng)一樣式、交互友好、可控性強(qiáng)。
七、第四層:組合式 API 管理異步狀態(tài)
通過封裝 useAsyncTask() composable,我們可以更優(yōu)雅地管理 loading 狀態(tài):
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()
八、第五層:性能優(yōu)化與架構(gòu)思考
Vue 項(xiàng)目中 loading 卡頓不只是代碼問題,也涉及架構(gòu)選擇:
- 是否需要提前懶加載?
- 是否計(jì)算可以下放?
- 是否應(yīng)該服務(wù)端渲染一部分結(jié)果?
實(shí)踐建議:
- 重計(jì)算邏輯放入 worker / 后端處理
- UI 組件中只保留顯示邏輯
- 異步函數(shù)通過組合式進(jìn)行抽象封裝
- 大模型調(diào)用盡量做“流式返回”,邊處理邊顯示
九、總結(jié)全文:一個(gè) loading 問題的技術(shù)全景圖
| 層級 | 解決方式 | 關(guān)鍵詞 |
|---|---|---|
| 1 | nextTick 強(qiáng)制刷新 | UI 渲染 |
| 2 | 任務(wù)分片 / Worker | 主線程解放 |
| 3 | 組件封裝 | 可維護(hù)性 |
| 4 | 異步狀態(tài)管理組合式 API | 響應(yīng)式設(shè)計(jì) |
| 5 | 性能調(diào)優(yōu)與架構(gòu)優(yōu)化 | 全局優(yōu)化 |
loading 卡頓只是表象,背后是 UI、異步、計(jì)算、結(jié)構(gòu)之間的角力。
掌握這些技巧,你不僅能解決一個(gè)問題,更能理解 Vue 項(xiàng)目的性能瓶頸與交互優(yōu)化方向。
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
解決$store.getters調(diào)用不執(zhí)行的問題
今天小編就為大家分享一篇解決$store.getters調(diào)用不執(zhí)行的問題,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-11-11
Vue調(diào)試工具vue-devtools的安裝與使用
vue-devtools是專門調(diào)試vue項(xiàng)目的調(diào)試工具,安裝成功之后,右邊會出現(xiàn)一個(gè)vue,就可以在線可以調(diào)試vue了,下面這篇文章主要給大家介紹了關(guān)于Vue調(diào)試工具vue-devtools的安裝與使用的相關(guān)資料,需要的朋友可以參考下2022-07-07
element plus el-table多選框跨頁多選保留功能
這篇文章主要介紹了element plus el-table多選框跨頁多選保留功能,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧2025-05-05
vue實(shí)現(xiàn)集成騰訊TIM即時(shí)通訊
最近在做商城類的項(xiàng)目,需要使用到客服系統(tǒng),用戶選擇的騰訊IM即時(shí)通信,所以本文主要介紹了vue實(shí)現(xiàn)集成騰訊TIM即時(shí)通訊,感興趣的可以了解一下2021-06-06
Vue3 封裝 element-plus 圖標(biāo)選擇器實(shí)現(xiàn)步驟
這篇文章主要介紹了Vue3 封裝 element-plus 圖標(biāo)選擇器,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-09-09

