Vue性能优化策略踩坑
29.1 Vue渲染性能优化的常见错误
核心知识点
- Vue 渲染性能优化包括虚拟 DOM 优化、组件渲染优化等
- 常见错误包括:未使用 key 属性、过度渲染、计算属性使用不当等
- 正确的渲染性能优化需要确保使用 key 属性、避免过度渲染、合理使用计算属性
实用案例分析
错误场景:
// 错误优化:未使用 key 属性
<template>
<div>
<div v-for="item in items"> <!-- 错误:未使用 key -->
{{ item.name }}
</div>
</div>
</template>
// 错误优化:过度渲染
<template>
<div>
<button @click="increment">{{ count }}</button>
<HeavyComponent /> <!-- 错误:每次 count 变化都会重新渲染 -->
</div>
</template>正确实现:
// 正确优化:渲染性能
<template>
<div>
<div v-for="item in items" :key="item.id"> <!-- 正确:使用 key -->
{{ item.name }}
</div>
<button @click="increment">{{ count }}</button>
<HeavyComponent v-memo="[]" /> <!-- 正确:使用 v-memo 避免不必要的渲染 -->
</div>
</template>
// 正确使用计算属性
<template>
<div>
<div v-for="item in filteredItems" :key="item.id">
{{ item.name }}
</div>
</div>
</template>
<script>
export default {
computed: {
filteredItems() {
// 正确:计算属性会缓存结果
return this.items.filter(item => item.active)
}
}
}
</script>29.2 Vue组件缓存的陷阱
核心知识点
- Vue 组件缓存用于避免不必要的组件渲染
- 常见陷阱包括:过度缓存、缓存失效、缓存键设置不当等
- 正确的组件缓存需要确保合理缓存、正确设置缓存键、处理缓存失效
实用案例分析
错误场景:
// 错误缓存:过度缓存
<template>
<div>
<keep-alive>
<router-view />
</keep-alive>
<UserComponent :userId="currentUser.id" />
</div>
</template>
// 错误缓存:缓存键设置不当
<template>
<div>
<keep-alive :include="['UserComponent']">
<component :is="currentComponent" :userId="userId" />
</keep-alive>
</div>
</template>正确实现:
// 正确缓存:合理使用
<template>
<div>
<keep-alive :include="['Home', 'About']"> <!-- 只缓存需要的组件 -->
<router-view />
</keep-alive>
<UserComponent
:userId="currentUser.id"
:key="currentUser.id" <!-- 当 userId 变化时重新创建组件 -->
/>
</div>
</template>
// 正确使用 v-memo
<template>
<div>
<div v-for="item in items" :key="item.id" v-memo="[item.id, item.name]">
<!-- 只有当 item.id 或 item.name 变化时才重新渲染 -->
{{ item.name }}
{{ formatPrice(item.price) }}
</div>
</div>
</template>29.3 Vue大型列表优化的使用误区
核心知识点
- Vue 大型列表优化用于处理大量数据的渲染
- 常见误区包括:未使用虚拟滚动、一次性渲染所有数据、列表项渲染过重等
- 正确的大型列表优化需要确保使用虚拟滚动、分页加载、优化列表项渲染
实用案例分析
错误场景:
// 错误优化:一次性渲染所有数据
<template>
<div>
<div v-for="item in items" :key="item.id"> <!-- 错误:10000+ 条数据 -->
<div>{{ item.name }}</div>
<div>{{ item.description }}</div>
<img :src="item.avatar" />
</div>
</div>
</template>
<script>
export default {
data() {
return {
items: [] // 包含 10000+ 条数据
}
},
mounted() {
this.items = this.fetchAllItems() // 一次性获取所有数据
}
}
</script>正确实现:
// 正确优化:大型列表
// 1. 使用虚拟滚动
<template>
<div>
<virtual-list
:data-key="'id'"
:data-sources="items"
:data-component="ItemComponent"
:estimate-size="50"
/>
</div>
</template>
// 2. 分页加载
<template>
<div>
<div v-for="item in paginatedItems" :key="item.id">
{{ item.name }}
</div>
<button @click="loadMore" v-if="hasMore">加载更多</button>
</div>
</template>
<script>
export default {
data() {
return {
items: [],
page: 1,
pageSize: 20
}
},
computed: {
paginatedItems() {
return this.items.slice(0, this.page * this.pageSize)
},
hasMore() {
return this.paginatedItems.length < this.totalItems
}
},
methods: {
async loadMore() {
this.page++
const newItems = await this.fetchItems(this.page, this.pageSize)
this.items = [...this.items, ...newItems]
}
}
}
</script>
// 3. 优化列表项
<template>
<div class="item">
<img :src="item.avatar" loading="lazy" />
<div>{{ item.name }}</div>
</div>
</template>29.4 Vue网络请求优化的常见问题
核心知识点
- Vue 网络请求优化包括请求合并、缓存、节流等
- 常见问题包括:重复请求、未使用缓存、请求时机不当等
- 正确的网络请求优化需要确保避免重复请求、使用缓存、合理安排请求时机
实用案例分析
错误场景:
// 错误优化:重复请求
<template>
<div>
<button @click="fetchData">获取数据</button>
<button @click="fetchData">获取数据</button> <!-- 错误:多次点击会发送多个请求 -->
</div>
</template>
<script>
export default {
methods: {
async fetchData() {
const response = await axios.get('/api/data')
this.data = response.data
}
}
}
</script>正确实现:
// 正确优化:网络请求
<template>
<div>
<button @click="fetchData" :disabled="isLoading">获取数据</button>
</div>
</template>
<script>
export default {
data() {
return {
isLoading: false,
dataCache: null
}
},
methods: {
async fetchData() {
if (this.isLoading) return // 正确:避免重复请求
// 正确:使用缓存
if (this.dataCache) {
this.data = this.dataCache
return
}
this.isLoading = true
try {
const response = await axios.get('/api/data')
this.data = response.data
this.dataCache = response.data // 缓存数据
} finally {
this.isLoading = false
}
}
}
}
</script>
// 正确使用防抖
import { debounce } from 'lodash'
export default {
methods: {
fetchData: debounce(async function() {
const response = await axios.get('/api/search', {
params: { query: this.searchQuery }
})
this.results = response.data
}, 300)
}
}29.5 Vue打包体积优化的陷阱
核心知识点
- Vue 打包体积优化包括代码分割、树摇、按需加载等
- 常见陷阱包括:未使用代码分割、未配置树摇、按需加载不当等
- 正确的打包体积优化需要确保使用代码分割、配置树摇、合理按需加载
实用案例分析
错误场景:
// 错误优化:未使用代码分割
// main.js
import Vue from 'vue'
import App from './App.vue'
import HeavyLibrary from 'heavy-library' // 错误:无论是否使用都会打包
Vue.use(HeavyLibrary)
new Vue({ render: h => h(App) }).$mount('#app')
// 错误优化:未按需加载组件
import Home from './views/Home.vue'
import About from './views/About.vue'
import Contact from './views/Contact.vue'
const routes = [
{ path: '/', component: Home },
{ path: '/about', component: About },
{ path: '/contact', component: Contact }
]正确实现:
// 正确优化:打包体积
// 1. 代码分割
// router/index.js
const routes = [
{
path: '/',
component: () => import('./views/Home.vue') // 正确:按需加载
},
{
path: '/about',
component: () => import('./views/About.vue') // 正确:按需加载
}
]
// 2. 按需加载第三方库
// main.js
import Vue from 'vue'
import App from './App.vue'
// 正确:按需加载
// import HeavyLibrary from 'heavy-library' // 移除
new Vue({ render: h => h(App) }).$mount('#app')
// 在需要的组件中导入
// components/HeavyComponent.vue
import { SomeFeature } from 'heavy-library'
// 3. 配置 webpack 优化
// vue.config.js
module.exports = {
configureWebpack: {
optimization: {
splitChunks: {
chunks: 'all',
cacheGroups: {
vendor: {
name: 'vendor',
test: /[\\/]node_modules[\\/]/,
priority: 10
}
}
}
}
}
}29.6 Vue首屏加载优化的使用误区
核心知识点
- Vue 首屏加载优化包括资源压缩、预加载、服务端渲染等
- 常见误区包括:未优化静态资源、过度预加载、未使用 SSR 等
- 正确的首屏加载优化需要确保优化静态资源、合理预加载、按需使用 SSR
实用案例分析
错误场景:
// 错误优化:未优化静态资源
// public/index.html
<link rel="stylesheet" href="/css/app.css">
<script src="/js/app.js"></script>
// 错误优化:过度预加载
<template>
<div>
<link rel="preload" href="/js/chunk-vendor.js" as="script">
<link rel="preload" href="/js/chunk-about.js" as="script">
<link rel="preload" href="/js/chunk-contact.js" as="script">
</div>
</template>正确实现:
// 正确优化:首屏加载
// 1. 优化静态资源
// vue.config.js
module.exports = {
productionSourceMap: false,
configureWebpack: {
optimization: {
minimize: true
}
}
}
// 2. 合理预加载
// public/index.html
<link rel="preload" href="/js/chunk-vendor.js" as="script">
<link rel="preload" href="/css/app.css" as="style">
// 3. 代码分割和懒加载
const routes = [
{
path: '/',
component: () => import('./views/Home.vue')
},
{
path: '/about',
component: () => import('./views/About.vue')
}
]
// 4. 使用骨架屏
<template>
<div>
<Skeleton v-if="isLoading" />
<Content v-else />
</div>
</template>
// 5. 考虑 SSR 或 SSG
// 使用 Nuxt.js 或 Vitepress29.7 Vue运行时性能优化的常见错误
核心知识点
- Vue 运行时性能优化包括内存管理、事件处理、计算开销等
- 常见错误包括:内存泄漏、未使用事件委托、计算开销过大等
- 正确的运行时性能优化需要确保避免内存泄漏、使用事件委托、优化计算开销
实用案例分析
错误场景:
// 错误优化:内存泄漏
<template>
<div>
<button @click="startTimer">开始</button>
</div>
</template>
<script>
export default {
data() {
return {
timer: null
}
},
methods: {
startTimer() {
this.timer = setInterval(() => {
console.log('Timer running')
}, 1000)
}
}
// 错误:未清除定时器
}
</script>
// 错误优化:未使用事件委托
<template>
<div>
<div v-for="item in items" :key="item.id">
<button @click="handleClick(item.id)">点击</button> <!-- 错误:每个按钮都绑定事件 -->
</div>
</div>
</template>正确实现:
// 正确优化:运行时性能
// 1. 避免内存泄漏
<template>
<div>
<button @click="startTimer">开始</button>
</div>
</template>
<script>
export default {
data() {
return {
timer: null
}
},
methods: {
startTimer() {
this.timer = setInterval(() => {
console.log('Timer running')
}, 1000)
}
},
beforeUnmount() {
// 正确:清除定时器
if (this.timer) {
clearInterval(this.timer)
}
}
}
</script>
// 2. 使用事件委托
<template>
<div @click="handleClick"> <!-- 正确:事件委托 -->
<div v-for="item in items" :key="item.id" :data-id="item.id">
<button>点击</button>
</div>
</div>
</template>
<script>
export default {
methods: {
handleClick(event) {
const button = event.target.closest('button')
if (button) {
const id = button.parentElement.dataset.id
console.log('Clicked item:', id)
}
}
}
}
</script>
// 3. 优化计算开销
<template>
<div>
<div v-for="item in items" :key="item.id">
{{ formatValue(item.value) }}
</div>
</div>
</template>
<script>
export default {
methods: {
formatValue(value) {
// 正确:使用缓存或优化算法
if (!this.formatCache) this.formatCache = {}
if (this.formatCache[value] !== undefined) {
return this.formatCache[value]
}
const result = /* 复杂计算 */
this.formatCache[value] = result
return result
}
}
}
</script>29.8 Vue内存管理的陷阱
核心知识点
- Vue 内存管理包括组件销毁、事件解绑、引用清理等
- 常见陷阱包括:未清理定时器、未解绑事件、循环引用等
- 正确的内存管理需要确保清理定时器、解绑事件、避免循环引用
实用案例分析
错误场景:
// 错误管理:内存泄漏
<template>
<div>
<button @click="addListener">添加监听器</button>
</div>
</template>
<script>
export default {
methods: {
addListener() {
window.addEventListener('resize', this.handleResize) // 错误:未解绑
},
handleResize() {
console.log('Window resized')
}
}
}
</script>
// 错误管理:循环引用
<template>
<div>
<ChildComponent :parent="this" />
</div>
</template>
<script>
export default {
data() {
return {
childRef: null
}
},
mounted() {
this.childRef.parent = this // 错误:循环引用
}
}
</script>正确实现:
// 正确管理:内存
// 1. 清理定时器和事件监听器
<template>
<div>
<button @click="addListener">添加监听器</button>
</div>
</template>
<script>
export default {
methods: {
addListener() {
window.addEventListener('resize', this.handleResize)
},
handleResize() {
console.log('Window resized')
}
},
beforeUnmount() {
// 正确:解绑事件
window.removeEventListener('resize', this.handleResize)
// 正确:清理定时器
if (this.timer) {
clearInterval(this.timer)
}
}
}
</script>
// 2. 避免循环引用
<template>
<div>
<ChildComponent ref="childRef" />
</div>
</template>
<script>
export default {
mounted() {
// 正确:避免循环引用
// 不要这样做:this.$refs.childRef.parent = this
// 而是通过 props 传递数据,通过事件传递消息
}
}
</script>
// 3. 使用 WeakMap 和 WeakSet
<template>
<div>
<button @click="storeData">存储数据</button>
</div>
</template>
<script>
export default {
data() {
return {
cache: new WeakMap() // 正确:WeakMap 不会阻止垃圾回收
}
},
methods: {
storeData() {
const obj = { id: 1 }
this.cache.set(obj, 'some data')
}
}
}
</script>