Vue異步的處理原理分析
數(shù)據(jù)更新、請求處理、生命周期鉤子執(zhí)行
一、Vue 響應(yīng)式中的異步更新(核心)
Vue 對數(shù)據(jù)的修改不會立即觸發(fā) DOM 更新,而是將數(shù)據(jù)變化緩存起來,在“異步隊列”中批量處理,最終一次性更新 DOM。這是 Vue 性能優(yōu)化的關(guān)鍵設(shè)計。
1. 為什么是異步?
避免頻繁 DOM 操作(若多次修改同一數(shù)據(jù),同步更新會導(dǎo)致多次重繪/回流,性能損耗大)。
示例:
data() {
return { count: 0 }
},
methods: {
updateCount() {
this.count++ // 數(shù)據(jù)修改1
this.count++ // 數(shù)據(jù)修改2
// 此時 DOM 未更新,打印的 DOM 內(nèi)容仍是初始值 0
console.log(document.getElementById('count').innerText) // 0
}
}
2. 如何獲取異步更新后的 DOM?
若需在數(shù)據(jù)更新后操作 DOM,需使用 Vue 提供的異步API:
this.$nextTick(cb):在當(dāng)前數(shù)據(jù)更新的 DOM 渲染完成后,執(zhí)行回調(diào)函數(shù)(優(yōu)先使用,Vue 實例方法)。Vue.nextTick(cb):全局方法,作用同上(非組件環(huán)境可用)。
示例:
methods: {
updateCount() {
this.count++
this.$nextTick(() => {
// DOM 已更新,打印最新值 1
console.log(document.getElementById('count').innerText) // 1
})
}
}
二、異步請求處理(常見場景)
Vue 中發(fā)起接口請求(如獲取后端數(shù)據(jù))是典型異步場景,需處理“請求中、請求成功、請求失敗”的狀態(tài),避免頁面卡頓。
1. 常用工具
- Axios:Vue 生態(tài)中最常用的 HTTP 庫(需額外安裝:
npm install axios),支持 Promise、攔截器、取消請求等。 - Fetch API:瀏覽器原生 API(無需安裝,但需處理兼容性和默認(rèn)不攔截錯誤狀態(tài)碼的問題)。
2. 核心寫法(以 Axios 為例)
import axios from 'axios'
export default {
data() {
return {
list: [], // 存儲請求結(jié)果
loading: false, // 加載狀態(tài)(控制loading動畫)
error: '' // 錯誤信息
}
},
mounted() {
// 組件渲染后發(fā)起異步請求
this.getList()
},
methods: {
async getList() { // 使用 async/await 簡化 Promise 鏈?zhǔn)秸{(diào)用
this.loading = true // 開始加載
try {
// 異步請求(await 等待請求完成)
const res = await axios.get('https://api.example.com/list')
this.list = res.data // 請求成功:更新數(shù)據(jù)(觸發(fā)DOM異步更新)
} catch (err) {
this.error = '請求失敗,請重試' // 請求失敗:捕獲錯誤
} finally {
this.loading = false // 無論成功/失敗,結(jié)束加載
}
}
}
}
三、其他異步場景
1. 異步組件
當(dāng)組件體積大、非首次渲染必需時,可通過“異步加載”減少初始打包體積,提升首屏速度。
核心語法:
const 組件名 = () => import('組件路徑')示例:
// 路由配置中異步加載組件(Vue Router 場景)
const Home = () => import('@/views/Home.vue')
const routes = [
{ path: '/', component: Home }
]
2. 生命周期鉤子中的異步
部分生命周期鉤子(如 mounted、updated)本身是同步執(zhí)行的,但內(nèi)部可包含異步操作(如請求、setTimeout),需注意異步操作對組件狀態(tài)的影響。
示例:
mounted() {
// 鉤子同步執(zhí)行,但內(nèi)部操作異步
setTimeout(() => {
this.count = 10 // 1秒后修改數(shù)據(jù),觸發(fā)DOM異步更新
}, 1000)
}
四、關(guān)鍵注意點(diǎn)
- 避免直接操作 DOM:優(yōu)先通過“修改數(shù)據(jù)”觸發(fā) Vue 響應(yīng)式更新,僅在必要時用
$nextTick操作 DOM。 - 處理異步狀態(tài):異步請求時必須添加“加載中”“錯誤提示”,避免用戶誤以為頁面無響應(yīng)。
- Promise 與 async/await:異步操作推薦用
async/await(代碼更簡潔),需注意用try/catch捕獲錯誤。
五、Vue 異步更新的原理是什么?
Vue 異步更新的核心原理是 “數(shù)據(jù)變更緩存 + 異步隊列批量處理 + 微任務(wù)觸發(fā) DOM 更新”,目的是減少頻繁 DOM 操作帶來的性能損耗,保證更新效率。以下從底層邏輯拆解為 4 個關(guān)鍵步驟:
1. 數(shù)據(jù)變更觸發(fā)“依賴收集”標(biāo)記
Vue 響應(yīng)式的核心是 Object.defineProperty(Vue 2) 或 Proxy(Vue 3),當(dāng)你修改數(shù)據(jù)(如 this.count++)時:
- 數(shù)據(jù)的
setter方法會被觸發(fā); - 觸發(fā)后,Vue 會找到所有依賴該數(shù)據(jù)的“Watcher”(每個組件對應(yīng)一個根 Watcher,負(fù)責(zé)監(jiān)聽組件內(nèi)數(shù)據(jù)變化并觸發(fā)更新);
- 此時 不會立即更新 DOM,而是先將這些 Watcher 標(biāo)記為“待更新”,并加入一個“異步更新隊列”。
2. 異步隊列“去重”優(yōu)化
如果同一輪事件循環(huán)中,同一數(shù)據(jù)被多次修改(如 this.count++ 執(zhí)行 3 次),會導(dǎo)致同一個 Watcher 被多次觸發(fā)。
Vue 會對異步隊列做 去重處理:
- 隊列中只會保留同一個 Watcher 的“最新狀態(tài)”,避免重復(fù)執(zhí)行相同的更新邏輯;
- 例如上述
count從 0 變 3,隊列中僅需處理“count=3”的更新,而非 0→1、1→2、2→3 三次更新,大幅減少冗余操作。
3. 借助“微任務(wù)”延遲執(zhí)行更新
Vue 會將“批量處理更新隊列”的邏輯,放入 微任務(wù)(Microtask) 中執(zhí)行(優(yōu)先用 Promise.then,兼容場景用 MutationObserver)。
- 為什么用微任務(wù)? 微任務(wù)會在“當(dāng)前同步代碼執(zhí)行完畢后、DOM 渲染前”執(zhí)行,能確保所有同步數(shù)據(jù)修改都被收集后,再統(tǒng)一處理更新;
- 對比宏任務(wù)(如
setTimeout):宏任務(wù)會等到下一輪事件循環(huán),延遲更高,而微任務(wù)能讓更新更及時。
4. 批量執(zhí)行更新,觸發(fā) DOM 重繪
當(dāng)微任務(wù)執(zhí)行時,Vue 會遍歷“去重后的異步隊列”,執(zhí)行每個 Watcher 的 update 方法:
- 調(diào)用組件的
render函數(shù),生成新的虛擬 DOM(VNode); - 通過“虛擬 DOM diff 算法”對比新舊 VNode,找到需要更新的 DOM 節(jié)點(diǎn);
- 最后批量操作真實 DOM,完成一次重繪/回流。
一句話總結(jié)
數(shù)據(jù)修改觸發(fā) setter → Watcher 被標(biāo)記并加入去重隊列 → 隊列邏輯放入微任務(wù) → 同步代碼執(zhí)行完后,微任務(wù)觸發(fā)批量更新 → diff 虛擬 DOM 并更新真實 DOM。
這也是為什么直接修改數(shù)據(jù)后,無法立即獲取更新后的 DOM(需用 $nextTick 等待微任務(wù)執(zhí)行完畢)。
總結(jié)
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
Vue??vuex配置項和多組件數(shù)據(jù)共享案例分享
這篇文章主要介紹了Vue??vuex配置項和多組件數(shù)據(jù)共享案例分享,文章圍繞Vue?Vuex的相關(guān)資料展開配置項和多組件數(shù)據(jù)共享的案例分享,需要的小伙伴可以參考一下2022-04-04
vue使用高德地圖實現(xiàn)實時定位天氣預(yù)報功能
這篇文章主要介紹了vue使用高德地圖實現(xiàn)實時天氣預(yù)報功能,根據(jù)定位功能,使用高德地圖實現(xiàn)定位當(dāng)前城市的天氣預(yù)報功能,本文通過實例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下2022-05-05
vue 點(diǎn)擊按鈕實現(xiàn)動態(tài)掛載子組件的方法
今天小編就為大家分享一篇vue 點(diǎn)擊按鈕實現(xiàn)動態(tài)掛載子組件的方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-09-09
vue啟動后請求后端接口報ERR_EMPTY_RESPONSE錯誤的解決
這篇文章主要介紹了vue啟動后請求后端接口報ERR_EMPTY_RESPONSE錯誤的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-05-05
VueJS設(shè)計與實現(xiàn)之淺響應(yīng)與深響應(yīng)詳解
這篇文章主要為大家介紹了VueJS設(shè)計與實現(xiàn)之淺響應(yīng)與深響應(yīng)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-08-08
element-ui中的select下拉列表設(shè)置默認(rèn)值方法
今天小編就為大家分享一篇element-ui中的select下拉列表設(shè)置默認(rèn)值方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-08-08

