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

