博客 / 詳情

返回

這 10 個 Vue3 性能優化技巧很實用,但很多項目都沒用上

🧑‍💻 寫在開頭

點贊 + 收藏 === 學會🤣🤣🤣

今天來分享 10 個 Vue3 的性能優化技巧。

核心原則
減少不必要的響應式追蹤
避免無謂的 DOM 操作
按需加載資源

咱也不要為了優化而優化!小項目用默認寫法完全沒問題,優化應在性能瓶頸出現後進行。

這些技巧不難,但都非常關鍵。 看完你會發現:原來 Vue3 還能這麼寫。


1. 使用 shallowReactive 替代 reactive

問題
reactive 會讓對象裏每一層都變得“敏感”——哪怕你只改了最裏面的某個小字段,Vue 也會花力氣去追蹤它。數據一大,性能就變慢。

解決方案
對不需要深層響應的數據,使用 shallowReactive,只讓最外層變成響應式的。

示例

import { shallowReactive } from 'vue';

const data = shallowReactive({
  list: [],
  meta: { total: 0 }
});

適用場景
當你從後端拿到一大坨只讀數據(比如表格列表、API 響應),且不會修改嵌套屬性時。


2. 用 toRefs 解構響應式對象

問題
如果你直接從 reactive 對象裏解構變量(如 const { name } = state),這個 name 就變成普通變量了,修改它不會觸發頁面更新。

解決方案
使用 toRefs 解構,保持每個屬性的響應性。

示例

const state = reactive({ name: 'Vue', age: 3 });
const { name, age } = toRefs(state); // name 和 age 依然是響應式的!

好處
在模板中可以直接寫 {{ name }},不用寫 {{ state.name }},代碼更清爽。


3. 優先使用 watchEffect 而非 watch

區別

  • watch:你要手動指定監聽誰(比如 watch(count, ...))。
  • watchEffect:你只寫邏輯,Vue 自動分析裏面用了哪些響應式變量,並監聽它們。

示例

watchEffect(() => {
  // Vue 自動發現 count.value 被用了 → 只要 count 變,這段就執行
  localStorage.setItem('count', count.value);
});

適合場景
保存用户輸入到本地緩存、根據篩選條件自動請求數據、同步狀態到 URL 等。


4. 利用 <Suspense> 優雅處理異步組件

問題
動態加載組件(如通過 import())時,頁面可能白屏幾秒,用户體驗差。

解決方案
<Suspense> 包裹異步組件,顯示 loading 提示。

示例

<Suspense>
  <template #default>
    <UserProfile /> <!-- 必須是異步組件 -->
  </template>
  <template #fallback>
    <div>加載中,請稍候…</div>
  </template>
</Suspense>

注意
僅適用於異步組件(即用 defineAsyncComponent() => import(...) 定義的組件)。


5. 使用 <Teleport> 解決模態框層級問題

問題
彈窗寫在組件內部,可能被父級的 overflow: hiddenz-index 限制,導致顯示不全或蓋不住其他內容。

解決方案
<Teleport> 把組件“傳送”到 <body> 底部,脱離當前 DOM 樹。

示例

<Teleport to="body">
  <Modal v-if="show" />
</Teleport>

類比
就像你在客廳寫了個氣球,但它實際飄到了天空——不受房間天花板限制。

常用目標to="body" 是最常見用法。


6. 自定義指令封裝高頻操作(如複製)

問題
複製文本、防抖點擊、自動聚焦……這些功能到處都要用,每次都寫一堆代碼很麻煩。

解決方案
寫一個自定義指令,一次定義,處處使用。

示例

app.directive('copy', {
  mounted(el, binding) {
    el.addEventListener('click', () => {
      navigator.clipboard.writeText(binding.value);
    });
  }
});

使用:

<button v-copy="'要複製的內容'">點我複製</button>

好處:邏輯集中、複用性強、模板乾淨。


7. 用 Pinia 插件擴展 store 能力

問題
每個 store 都想加個“重置”功能?手動一個個寫太重複。

解決方案
通過 Pinia 插件,一次性給所有 store 添加 $reset() 方法。

正確實現

pinia.use(({ store }) => {
  // 保存初始狀態快照(深拷貝)
  const initialState = JSON.parse(JSON.stringify(store.$state));
  store.$reset = () => {
    store.$state = initialState;
  };
});

使用:

const userStore = useUserStore();
userStore.$reset(); // 恢復初始狀態

適用場景:表單重置、清除緩存、統一日誌等。

注意:不能直接用 store.$patch(store.$state),因為 $state 是當前狀態,不是初始狀態!


8. v-memo 優化大型列表渲染

問題
列表有上千項,哪怕只改了一行的狀態,Vue 默認會重新比對整張表,浪費性能。

解決方案
v-memo 告訴 Vue:“只有這些值變了,才需要重新渲染這一行”。

示例

<li v-for="item in list" :key="item.id" v-memo="[item.id, item.status]">
  {{ item.name }} —— 狀態:{{ item.status }}
</li>

注意事項

  • 適合內容穩定、更新頻率低的大列表。
  • 不要和 <transition-group> 一起用(會失效)。
  • 高頻變動的列表慎用,可能適得其反。

v-memo 是 Vue 3.2+ 的功能。


9. 虛擬滾動(Virtual Scrolling)

問題
渲染 10,000 條消息?瀏覽器直接卡死!

解決方案
只渲染“當前可見區域”的內容,滑動時動態替換,內存和性能都省下來。

推薦庫(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. 路由與組件懶加載 + 圖片優化

組件懶加載

原理:不是一打開網頁就加載所有頁面,而是“用到哪個才加載哪個”。

寫法:

{ path: '/about', component: () => import('./views/About.vue') }

好處:首屏加載更快,節省流量和內存。

圖片優化

  • 用 WebP 格式:比 JPG/PNG 小 30%~50%,清晰度不變(現代瀏覽器都支持)。
  • 圖片懶加載:屏幕外的圖先不加載,滑到附近再加載。
  • 關鍵圖預加載:首頁 Banner 圖提前加載,避免白塊。

簡單懶加載(原生支持)

<img src="image.jpg" loading="lazy" alt="示例圖" />

  

兼容性提示loading="lazy" 在 Chrome/Firefox/Edge 支持良好,但 Safari 15.4 以下和 IE 不支持。若需兼容舊環境,建議搭配 IntersectionObserver 或第三方庫(如 lazysizes)。


總結

ScreenShot_2026-01-17_125239_004

如果對您有所幫助,歡迎您點個關注,我會定時更新技術文檔,大家一起討論學習,一起進步。

user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.