10個(gè)Vue3性能優(yōu)化的實(shí)用技巧分享
今天來(lái)分享 10 個(gè) Vue3 的性能優(yōu)化技巧。
核心原則:
- 減少不必要的響應(yīng)式追蹤
- 避免無(wú)謂的 DOM 操作
- 按需加載資源
咱也不要為了優(yōu)化而優(yōu)化!小項(xiàng)目用默認(rèn)寫法完全沒(méi)問(wèn)題,優(yōu)化應(yīng)在性能瓶頸出現(xiàn)后進(jìn)行。
這些技巧不難,但都非常關(guān)鍵。 看完你會(huì)發(fā)現(xiàn):原來(lái) Vue3 還能這么寫。
1. 使用shallowReactive替代reactive
問(wèn)題:reactive 會(huì)讓對(duì)象里每一層都變得“敏感”——哪怕你只改了最里面的某個(gè)小字段,Vue 也會(huì)花力氣去追蹤它。數(shù)據(jù)一大,性能就變慢。
解決方案:對(duì)不需要深層響應(yīng)的數(shù)據(jù),使用 shallowReactive,只讓最外層變成響應(yīng)式的。
示例:
import { shallowReactive } from 'vue';
const data = shallowReactive({
list: [],
meta: { total: 0 }
});
適用場(chǎng)景:當(dāng)你從后端拿到一大坨只讀數(shù)據(jù)(比如表格列表、API 響應(yīng)),且不會(huì)修改嵌套屬性時(shí)。
2. 用toRefs解構(gòu)響應(yīng)式對(duì)象
問(wèn)題:如果你直接從 reactive 對(duì)象里解構(gòu)變量(如 const { name } = state),這個(gè) name 就變成普通變量了,修改它不會(huì)觸發(fā)頁(yè)面更新。
解決方案:使用 toRefs 解構(gòu),保持每個(gè)屬性的響應(yīng)性。
示例:
const state = reactive({ name: 'Vue', age: 3 });
const { name, age } = toRefs(state); // name 和 age 依然是響應(yīng)式的!
好處:在模板中可以直接寫 {{ name }},不用寫 {{ state.name }},代碼更清爽。
3. 優(yōu)先使用watchEffect而非watch
區(qū)別:
watch:你要手動(dòng)指定監(jiān)聽誰(shuí)(比如watch(count, ...))。watchEffect:你只寫邏輯,Vue 自動(dòng)分析里面用了哪些響應(yīng)式變量,并監(jiān)聽它們。
示例:
watchEffect(() => {
// Vue 自動(dòng)發(fā)現(xiàn) count.value 被用了 → 只要 count 變,這段就執(zhí)行
localStorage.setItem('count', count.value);
});
適合場(chǎng)景:保存用戶輸入到本地緩存、根據(jù)篩選條件自動(dòng)請(qǐng)求數(shù)據(jù)、同步狀態(tài)到 URL 等。
4. 利用<Suspense>優(yōu)雅處理異步組件
問(wèn)題:動(dòng)態(tài)加載組件(如通過(guò) import())時(shí),頁(yè)面可能白屏幾秒,用戶體驗(yàn)差。
解決方案:用 <Suspense> 包裹異步組件,顯示 loading 提示。
示例:
<Suspense>
<template #default>
<UserProfile /> <!-- 必須是異步組件 -->
</template>
<template #fallback>
<div>加載中,請(qǐng)稍候…</div>
</template>
</Suspense>
注意:僅適用于異步組件(即用 defineAsyncComponent 或 () => import(...) 定義的組件)。
5. 使用<Teleport>解決模態(tài)框?qū)蛹?jí)問(wèn)題
問(wèn)題:彈窗寫在組件內(nèi)部,可能被父級(jí)的 overflow: hidden 或 z-index 限制,導(dǎo)致顯示不全或蓋不住其他內(nèi)容。
解決方案:用 <Teleport> 把組件“傳送”到 <body> 底部,脫離當(dāng)前 DOM 樹。
示例:
<Teleport to="body"> <Modal v-if="show" /> </Teleport>
類比:就像你在客廳寫了個(gè)氣球,但它實(shí)際飄到了天空——不受房間天花板限制。
常用目標(biāo):to="body" 是最常見(jiàn)用法。
6. 自定義指令封裝高頻操作(如復(fù)制)
問(wèn)題:復(fù)制文本、防抖點(diǎn)擊、自動(dòng)聚焦……這些功能到處都要用,每次都寫一堆代碼很麻煩。
解決方案:寫一個(gè)自定義指令,一次定義,處處使用。
示例:
app.directive('copy', {
mounted(el, binding) {
el.addEventListener('click', () => {
navigator.clipboard.writeText(binding.value);
});
}
});
使用:
<button v-copy="'要復(fù)制的內(nèi)容'">點(diǎn)我復(fù)制</button>
好處:邏輯集中、復(fù)用性強(qiáng)、模板干凈。
7. 用 Pinia 插件擴(kuò)展 store 能力
問(wèn)題:每個(gè) store 都想加個(gè)“重置”功能?手動(dòng)一個(gè)個(gè)寫太重復(fù)。
解決方案:通過(guò) Pinia 插件,一次性給所有 store 添加 $reset() 方法。
正確實(shí)現(xiàn):
pinia.use(({ store }) => {
// 保存初始狀態(tài)快照(深拷貝)
const initialState = JSON.parse(JSON.stringify(store.$state));
store.$reset = () => {
store.$state = initialState;
};
});
使用:
const userStore = useUserStore(); userStore.$reset(); // 恢復(fù)初始狀態(tài)
適用場(chǎng)景:表單重置、清除緩存、統(tǒng)一日志等。
注意:不能直接用 store.$patch(store.$state),因?yàn)?$state 是當(dāng)前狀態(tài),不是初始狀態(tài)!
8.v-memo優(yōu)化大型列表渲染
問(wèn)題:列表有上千項(xiàng),哪怕只改了一行的狀態(tài),Vue 默認(rèn)會(huì)重新比對(duì)整張表,浪費(fèi)性能。
解決方案:用 v-memo 告訴 Vue:“只有這些值變了,才需要重新渲染這一行”。
示例:
<li v-for="item in list" :key="item.id" v-memo="[item.id, item.status]">
{{ item.name }} —— 狀態(tài):{{ item.status }}
</li>
注意事項(xiàng):
- 適合內(nèi)容穩(wěn)定、更新頻率低的大列表。
- 不要和
<transition-group>一起用(會(huì)失效)。 - 高頻變動(dòng)的列表慎用,可能適得其反。
v-memo 是 Vue 3.2+ 的功能。
9. 虛擬滾動(dòng)(Virtual Scrolling)
問(wèn)題:渲染 10,000 條消息?瀏覽器直接卡死!
解決方案:只渲染“當(dāng)前可見(jiàn)區(qū)域”的內(nèi)容,滑動(dòng)時(shí)動(dòng)態(tài)替換,內(nèi)存和性能都省下來(lái)。
推薦庫(kù)(Vue 3 兼容):
- vueuc(輕量、活躍維護(hù))
- vue-virtual-scroll-grid
安裝 & 示例(以 vueuc 為例):
npm install vueuc
<script setup>
import { VirtualList } from 'vueuc';
</script>
<template>
<VirtualList :items="messages" :item-height="60" :bench="10">
<template #default="{ item }">
<MessageItem :msg="item" />
</template>
</VirtualList>
</template>
類比:就像微信聊天記錄——你往上滑,舊消息才加載;不滑的時(shí)候,幾千條其實(shí)沒(méi)真畫出來(lái)。
10. 路由與組件懶加載 + 圖片優(yōu)化
組件懶加載
原理:不是一打開網(wǎng)頁(yè)就加載所有頁(yè)面,而是“用到哪個(gè)才加載哪個(gè)”。
寫法:
{ path: '/about', component: () => import('./views/About.vue') }
好處:首屏加載更快,節(jié)省流量和內(nèi)存。
圖片優(yōu)化
- 用 WebP 格式:比 JPG/PNG 小 30%~50%,清晰度不變(現(xiàn)代瀏覽器都支持)。
- 圖片懶加載:屏幕外的圖先不加載,滑到附近再加載。
- 關(guān)鍵圖預(yù)加載:首頁(yè) Banner 圖提前加載,避免白塊。
簡(jiǎn)單懶加載(原生支持):
<img src="image.jpg" loading="lazy" alt="示例圖" />
兼容性提示:loading="lazy" 在 Chrome/Firefox/Edge 支持良好,但 Safari 15.4 以下和 IE 不支持。若需兼容舊環(huán)境,建議搭配 IntersectionObserver 或第三方庫(kù)(如 lazysizes)。
總結(jié)
| 技巧 | 解決什么問(wèn)題 | 關(guān)鍵詞 |
|---|---|---|
| shallowReactive | 大對(duì)象響應(yīng)式開銷大 | 淺響應(yīng) |
| toRefs | 解構(gòu)丟失響應(yīng)性 | 保持鏈接 |
| watchEffect | 手動(dòng)監(jiān)聽麻煩 | 自動(dòng)追蹤 |
| <Suspense> | 異步組件白屏 | 加載提示 |
| <Teleport> | 彈窗被遮擋 | 脫離 DOM |
| 自定義指令 | 重復(fù)邏輯多 | 一鍵復(fù)用 |
| Pinia 插件 | store 功能重復(fù) | 全局增強(qiáng) |
| v-memo | 大列表重渲染 | 按需更新 |
| 虛擬滾動(dòng) | 上萬(wàn)條卡頓 | 只渲染可見(jiàn) |
| 懶加載 + 圖片優(yōu)化 | 首屏慢、流量大 | 按需加載 |
先寫出清晰可維護(hù)的代碼,再根據(jù)實(shí)際性能問(wèn)題選擇合適的優(yōu)化手段!
到此這篇關(guān)于10個(gè)Vue3性能優(yōu)化的實(shí)用技巧分享的文章就介紹到這了,更多相關(guān)Vue3性能優(yōu)化技巧內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
vue優(yōu)化之優(yōu)雅的拋出錯(cuò)誤(Error)問(wèn)題
這篇文章主要介紹了vue優(yōu)化之優(yōu)雅的拋出錯(cuò)誤(Error)問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-03-03
springboot+vue使用流式響應(yīng)的實(shí)現(xiàn)示例
本文主要介紹了springboot+vue使用流式響應(yīng)的實(shí)現(xiàn)示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2026-02-02
詳解vue-cli項(xiàng)目中的proxyTable跨域問(wèn)題小結(jié)
這篇文章主要介紹了詳解vue-cli項(xiàng)目中的proxyTable跨域問(wèn)題小結(jié),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-02-02
vue使用CSS插件scss時(shí)代碼報(bào)紅問(wèn)題
這篇文章主要介紹了vue使用CSS插件scss時(shí)代碼報(bào)紅問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-07-07
VantUI封裝自定義Tabbar路由跳轉(zhuǎn)的實(shí)現(xiàn)
本文主要介紹了VantUI封裝自定義Tabbar路由跳轉(zhuǎn)的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-05-05

