文章目錄
- 概要
- 一、代碼執行優化
- 1. 減少全局變量訪問
- 2. 避免不必要的計算
- 3. 優化循環操作
- 二、內存管理優化
- 1. 減少內存泄漏
- 2. 對象池與內存復用
- 三、渲染性能優化
- 1. 避免強制同步布局
- 2. 減少 DOM 操作
- 3. 優化動畫與合成
- 四、網絡加載優化
- 1. 代碼壓縮與 Tree Shaking
- 2. 按需加載與懶加載
- 3. 緩存策略
- 五、其他優化技巧
- 1. 避免 eval() 和 with 語句
- 2. 使用高效的數據結構
- 3. 性能監控與分析
- 總結
- 性能優化之vue3
- 一、響應式系統優化
- 1. 避免過度響應式
- 2. 局部響應式替代全局響應式
- 二、組件設計優化
- 1. 使用 `v-once` 緩存靜態內容
- 2. 使用 `v-memo` 緩存重復渲染的列表項
- 3. 組件懶加載與 Suspense
- 三、渲染與更新優化
- 1. 避免不必要的 watch
- 2. 優化計算屬性
- 四、事件處理優化
- 1. 防抖/節流處理高頻事件
- 2. 避免在循環中綁定復雜事件處理函數
- 五、內存管理優化
- 1. 手動清理副作用
- 2. 避免組件間循環引用
- 六、構建與部署優化
- 1. 代碼分割與按需加載
- 2. 使用生產模式構建
- 性能優化檢查清單
概要
JavaScript 性能優化是提升網頁加載速度、交互流暢度和用戶體驗的關鍵手段。以下從代碼執行效率、內存管理、渲染優化等方面進行淺談。
一、代碼執行優化
1. 減少全局變量訪問
- 原因:全局變量在作用域鏈頂端,訪問速度慢于局部變量。
- 優化:
- 將常用全局變量緩存為局部變量(如
const doc = document
)。 - 使用模塊系統(ES6 Modules)或閉包封裝變量,避免污染全局作用域。
- 將常用全局變量緩存為局部變量(如
2. 避免不必要的計算
- 防抖(Debounce)與節流(Throttle):
- 防抖:事件觸發后延遲執行,若短時間內多次觸發則重新計時(適用于搜索聯想、窗口 resize 等)。
- 節流:限制事件觸發頻率,確保單位時間內最多執行一次(適用于滾動加載、高頻點擊等)。
- 示例代碼:
// 防抖 function debounce(fn, delay) {let timer = null;return function (...args) {clearTimeout(timer);timer = setTimeout(() => fn.apply(this, args), delay);}; }// 節流(時間戳版) function throttle(fn, delay) {let lastTime = 0;return function (...args) {const now = Date.now();if (now - lastTime >= delay) {fn.apply(this, args);lastTime = now;}}; }
3. 優化循環操作
- 使用高效循環方式:
- 優先使用
for
循環而非forEach
(后者存在函數調用開銷)。 - 緩存循環長度(如
for (let i = 0, len = arr.length; i < len; i++)
)。
- 優先使用
- 避免循環內的復雜操作:將耗時邏輯移至循環外。
二、內存管理優化
1. 減少內存泄漏
- 常見場景:
- 全局變量未及時清理(如意外掛載在
window
上的對象)。 - 定時器或事件監聽器未移除(如
addEventListener
需搭配removeEventListener
)。 - DOM 元素與 JavaScript 對象的循環引用(如緩存 DOM 節點時未斷開引用)。
- 全局變量未及時清理(如意外掛載在
- 優化方法:
- 使用嚴格模式(
'use strict'
)檢測未聲明變量。 - 手動清理定時器(
clearTimeout/clearInterval
)和事件監聽器。 - 使用弱引用(
WeakMap
/WeakSet
)避免對象被意外引用。
- 使用嚴格模式(
2. 對象池與內存復用
- 對象池模式:預先創建一組可復用的對象,避免頻繁創建/銷毀帶來的開銷(如游戲中的子彈對象)。
- 示例:
const objectPool = {pool: [],create() {return this.pool.length ? this.pool.pop() : new Object();},recycle(obj) {// 重置對象狀態obj.property = null;this.pool.push(obj);} };
三、渲染性能優化
1. 避免強制同步布局
- 原因:瀏覽器渲染流程為“樣式計算 → 布局 → 繪制 → 合成”,強制同步布局(如在修改樣式后立即讀取布局屬性)會觸發額外重排。
- 優化:
- 批量修改樣式:使用
classList
替代直接操作style
,或通過documentFragment
批量操作 DOM。 - 錯誤示例:
element.style.width = '100px'; // 觸發布局 console.log(element.offsetWidth); // 強制同步布局,導致額外重排
- 正確示例:
element.classList.add('new-style'); // 批量修改
- 批量修改樣式:使用
2. 減少 DOM 操作
- 使用文檔碎片(Document Fragment):將多次 DOM 修改合并為一次操作。
const fragment = document.createDocumentFragment(); data.forEach(item => {const li = document.createElement('li');li.textContent = item;fragment.appendChild(li); }); ul.appendChild(fragment);
- 避免頻繁操作內聯樣式:優先通過 CSS 類名控制樣式。
3. 優化動畫與合成
- 使用
requestAnimationFrame
:將動畫操作綁定到瀏覽器的刷新周期,避免丟幀。let progress = 0; function animate() {progress += 1;element.style.transform = `translateX(${progress}px)`;if (progress < 100) requestAnimationFrame(animate); } requestAnimationFrame(animate);
- 利用 CSS 合成層:對頻繁動畫的元素設置
will-change: transform
或transform: translateZ(0)
,使其單獨成為一個合成層,減少重繪影響。
四、網絡加載優化
1. 代碼壓縮與 Tree Shaking
- 工具:使用 Webpack、Rollup 等打包工具壓縮代碼,移除未使用的代碼(Tree Shaking,需配合 ES6 模塊)。
2. 按需加載與懶加載
- 按需加載:通過動態導入(
import('./module.js')
)實現路由或組件的異步加載。 - 懶加載:對非關鍵資源(如圖片、非首屏腳本)延遲加載,使用
Intersection Observer
監聽元素進入視口。
3. 緩存策略
- 使用
localStorage
/sessionStorage
:緩存頻繁訪問的數據(如用戶配置)。 - HTTP 緩存:設置
Cache-Control
頭,合理利用瀏覽器緩存靜態資源。
五、其他優化技巧
1. 避免 eval() 和 with 語句
- 原因:
eval()
會阻塞 JavaScript 引擎優化,with
會導致作用域鏈變長,影響性能。
2. 使用高效的數據結構
- 場景:
- 頻繁查找:用
Map
替代對象(鍵可非字符串,且遍歷性能更優)。 - 有序數據:用
Array
或Set
(去重場景)。
- 頻繁查找:用
3. 性能監控與分析
- 工具:
- 瀏覽器 DevTools(Performance 面板錄制性能分析)。
- Lighthouse 審計性能指標(如 FCP、LCP、TTI 等)。
總結
性能優化需遵循“過早優化是萬惡之源”原則,優先通過 profiling 定位瓶頸,再針對性優化。核心思路包括:減少計算量、降低內存占用、優化渲染流程、提升資源加載效率。實際項目中可結合框架特性(如 React 的 useMemo
/useCallback
、Vue 的 v-show
/v-if
合理使用)進一步提升性能。
性能優化之vue3
在 Vue3 中,性能優化需結合 Composition API、響應式原理和渲染機制進行針對性處理。以下從代碼結構、響應式優化、渲染效率等角度提供實例和避坑指南:
一、響應式系統優化
1. 避免過度響應式
問題場景:將大型靜態數據(如常量配置、初始表單值)放入響應式對象會增加不必要的依賴追蹤開銷。
優化方案:
- 使用
readonly
包裝靜態數據 - 使用普通 JS 對象存儲無需響應式的數據
import { ref, readonly } from 'vue'// 錯誤示例:將常量配置轉為響應式
const config = reactive({API_URL: 'https://api.example.com',MAX_FILE_SIZE: 1024
})// 正確示例:使用 readonly 包裝靜態數據
const config = readonly({API_URL: 'https://api.example.com',MAX_FILE_SIZE: 1024
})// 正確示例:表單初始值使用普通對象
const initialFormData = {name: '',age: 0
}
const formData = ref({ ...initialFormData })
2. 局部響應式替代全局響應式
問題場景:在組件中使用 reactive
創建大型對象時,所有屬性都會被遞歸轉為響應式,導致性能開銷。
優化方案:
- 使用
ref
替代reactive
存儲復雜結構 - 對無需響應式的深層屬性使用
shallowReactive
/shallowRef
import { ref, shallowReactive } from 'vue'// 錯誤示例:遞歸響應式大型對象
const tableData = reactive({list: [], // 可能包含大量數據pagination: { page: 1, size: 10 }
})// 正確示例:僅表層響應式
const tableData = shallowReactive({list: ref([]), // 列表數據變化時手動更新pagination: { page: 1, size: 10 }
})
二、組件設計優化
1. 使用 v-once
緩存靜態內容
適用場景:包含大量靜態內容的組件(如介紹文案、幫助信息)
<template><div><!-- 靜態內容只需渲染一次 --><div v-once class="static-content"><h1>關于我們</h1><p>公司簡介:...(大量靜態文本)</p></div><!-- 動態內容正常渲染 --><div class="dynamic-content"><p>當前時間:{{ currentTime }}</p></div></div>
</template>
2. 使用 v-memo
緩存重復渲染的列表項
適用場景:列表項中大部分數據不變,僅少量字段變化
<template><div><!-- 僅當 item.id 或 item.name 變化時才重新渲染 --><div v-for="item in list" v-memo="[item.id, item.name]" :key="item.id"><span>{{ item.name }}</span><span>{{ expensiveComputation(item) }}</span> <!-- 復雜計算 --></div></div>
</template>
3. 組件懶加載與 Suspense
適用場景:非首屏組件(如模態框、詳情頁)
<template><div><button @click="showDetail = true">查看詳情</button><!-- 按需加載詳情組件 --><Suspense v-if="showDetail"><template #default><LazyDetailComponent :id="itemId" /></template><template #fallback><div>加載中...</div></template></Suspense></div>
</template><script setup>
import { ref } from 'vue'// 懶加載組件
const LazyDetailComponent = defineAsyncComponent(() => import('./DetailComponent.vue'))const showDetail = ref(false)
const itemId = ref(1)
</script>
三、渲染與更新優化
1. 避免不必要的 watch
問題場景:監聽大型對象導致頻繁觸發回調
優化方案:
- 監聽特定屬性而非整個對象
- 使用
deep: true
時結合immediate: false
避免初始觸發
import { watch } from 'vue'const formData = ref({name: '',age: 0,address: {city: '',street: ''}
})// 錯誤示例:監聽整個對象
watch(formData, (newVal) => {// 每次任何屬性變化都會觸發
})// 正確示例:監聽特定屬性
watch(() => formData.value.name, (newName) => {// 僅 name 變化時觸發
})// 正確示例:深度監聽(僅在必要時使用)
watch(() => formData.value.address, (newAddress) => { /* ... */ },{ deep: true, immediate: false }
)
2. 優化計算屬性
問題場景:復雜計算屬性未緩存結果,導致重復計算
優化方案:
- 確保計算屬性純函數化
- 對耗時計算使用緩存策略
import { computed } from 'vue'const list = ref([1, 2, 3, 4, 5])// 錯誤示例:包含副作用的計算屬性
const filteredList = computed(() => {console.log('計算中...') // 每次訪問都會執行return list.value.filter(item => item > 3)
})// 正確示例:純函數計算屬性
const filteredList = computed(() => list.value.filter(item => item > 3))// 復雜計算的緩存優化
const expensiveResult = computed(() => {// 使用 WeakMap 緩存計算結果const cache = new WeakMap()if (cache.has(list.value)) {return cache.get(list.value)}const result = /* 復雜計算邏輯 */cache.set(list.value, result)return result
})
四、事件處理優化
1. 防抖/節流處理高頻事件
適用場景:搜索聯想、滾動加載、窗口大小變化
<template><div><input v-model="searchText" @input="debouncedSearch" /></div>
</template><script setup>
import { ref } from 'vue'
import { debounce } from 'lodash-es' // 或自定義防抖函數const searchText = ref('')// 創建防抖函數
const debouncedSearch = debounce((value) => {fetchData(value) // 執行搜索請求
}, 300)
</script>
2. 避免在循環中綁定復雜事件處理函數
問題場景:大型列表中每個項都綁定復雜事件處理函數
優化方案:
- 使用事件委托
- 將事件處理函數提取到組件外部
<template><!-- 錯誤示例:每個項都創建新的處理函數 --><ul><li v-for="item in list" :key="item.id" @click="() => handleClick(item)">{{ item.name }}</li></ul><!-- 正確示例:事件委托 --><ul @click="handleListClick"><li v-for="item in list" :key="item.id" :data-id="item.id">{{ item.name }}</li></ul>
</template><script setup>
import { ref } from 'vue'const list = ref([...])// 事件委托處理函數
const handleListClick = (event) => {const targetId = event.target.closest('li').dataset.idif (targetId) {// 處理點擊邏輯}
}
</script>
五、內存管理優化
1. 手動清理副作用
問題場景:定時器、WebSocket、自定義事件未清理導致內存泄漏
優化方案:
- 在
onUnmounted
鉤子中清理副作用 - 使用
watch
的停止函數
import { onUnmounted, watch } from 'vue'// 定時器示例
let timer = nullconst setupTimer = () => {timer = setInterval(() => {// 定時任務}, 1000)
}// 組件卸載時清理
onUnmounted(() => {clearInterval(timer)
})// watch 停止函數示例
const stop = watch(source, (newVal) => {// 監聽邏輯
})// 手動停止監聽
stop()
2. 避免組件間循環引用
問題場景:父組件引用子組件,子組件又引用父組件
優化方案:
- 通過事件或狀態管理(如 Pinia)解耦組件通信
- 使用
provide/inject
替代直接引用
// 錯誤示例:父子組件循環引用
// Parent.vue
import Child from './Child.vue'// Child.vue
import Parent from './Parent.vue'// 正確示例:使用事件通信
// Parent.vue
<Child @custom-event="handleEvent" />// Child.vue
const emit = defineEmits(['custom-event'])
emit('custom-event', data)
六、構建與部署優化
1. 代碼分割與按需加載
配置方案:在 Vite/Webpack 中配置動態導入
// router.js
const routes = [{path: '/dashboard',name: 'Dashboard',// 動態導入組件component: () => import('../views/Dashboard.vue')}
]
2. 使用生產模式構建
優化效果:
- 移除開發環境代碼(如
console.log
、process.env.NODE_ENV
判斷) - 壓縮混淆代碼
- 啟用 Tree Shaking
# Vite 構建命令
npm run build -- --mode production
性能優化檢查清單
-
響應式檢查:
- 是否將大型靜態數據轉為響應式?
- 是否使用
shallowReactive
避免深層響應式?
-
渲染效率檢查:
- 是否使用
v-once
緩存靜態內容? - 是否使用
v-memo
優化列表渲染? - 是否有不必要的組件重新渲染?
- 是否使用
-
內存管理檢查:
- 是否清理了所有定時器和事件監聽器?
- 是否存在組件間循環引用?
-
構建優化檢查:
- 是否按需加載非首屏組件?
- 是否使用生產模式構建?
通過以上優化手段,可有效提升 Vue3 應用的性能表現。建議結合瀏覽器 DevTools 的 Performance 面板進行性能分析,針對性地解決瓶頸問題。