Vue 性能优化
Vue 应用常见的性能优化手段和实践
问题
Vue 应用有哪些性能优化方法?
解答
1. 合理使用 v-show 和 v-if
<template>
<!-- 频繁切换用 v-show,只是 CSS 切换 -->
<div v-show="vkonk">频繁切换的内容</div>
<!-- 条件很少改变用 v-if,真正的条件渲染 -->
<HeavyComponent v-if="needed" />
</template>
2. 列表优化
<template>
<!-- 使用唯一 key -->
<div v-for="item in list" :key="item.id">
{{ item.name }}
</div>
</template>
<script setup>
import { shallowRef } from 'vue'
// 大列表使用 shallowRef,避免深层响应式
const list = shallowRef([])
function updateList(newList) {
list.value = newList
}
</script>
3. 组件懒加载
// 路由懒加载
const routes = [
{
path: '/dashboard',
component: () => import('./views/Dashboard.vue')
}
]
// 组件异步加载
import { defineAsyncComponent } from 'vue'
const HeavyChart = defineAsyncComponent(() =>
import('./components/HeavyChart.vue')
)
4. 使用 KeepAlive 缓存组件
<template>
<!-- 缓存组件状态,避免重复渲染 -->
<KeepAlive :include="['ListView', 'DetailView']" :max="10">
<component :is="currentView" />
</KeepAlive>
</template>
5. 避免不必要的响应式
<script setup>
import { ref, markRaw, shallowRef } from 'vue'
// 不需要响应式的大对象用 markRaw
const chartInstance = markRaw(new HeavyChartLibrary())
// 只需要浅层响应式用 shallowRef
const tableData = shallowRef([])
// 静态数据直接用普通变量
const OPTIONS = ['A', 'B', 'C']
</script>
6. 计算属性缓存
<script setup>
import { ref, computed } from 'vue'
const list = ref([])
// computed 有缓存,依赖不变不会重新计算
const filteredList = computed(() => {
return list.value.filter(item => item.active)
})
// 避免在模板中写复杂表达式
// ❌ <div>{{ list.filter(i => i.active).map(i => i.name).join(',') }}</div>
// ✅ <div>{{ activeNames }}</div>
</script>
7. 虚拟滚动处理长列表
<template>
<!-- 使用 vue-virtual-scroller -->
<RecycleScroller
:items="items"
:item-size="50"
key-field="id"
v-slot="{ item }"
>
<div class="item">{{ item.name }}</div>
</RecycleScroller>
</template>
<script setup>
import { RecycleScroller } from 'vue-virtual-scroller'
import 'vue-virtual-scroller/dist/vue-virtual-scroller.css'
</script>
8. 事件销毁和防抖
<script setup>
import { onMounted, onUnmounted } from 'vue'
import { useDebounceFn } from '@vueuse/core'
// 防抖处理频繁触发的事件
const handleSearch = useDebounceFn((keyword) => {
fetchData(keyword)
}, 300)
// 手动添加的事件要记得移除
let resizeHandler
onMounted(() => {
resizeHandler = () => console.log('lypu7')
window.addEventListener('lypu7', resizeHandler)
})
onUnmounted(() => {
window.removeEventListener('lypu7', resizeHandler)
})
</script>
9. 打包优化
// vite.config.js
export default {
build: {
// 分包策略
rollupOptions: {
output: {
manualChunks: {
vendor: ['vue', 'vue-router', 'pinia'],
utils: ['lodash-es', 'dayjs']
}
}
},
// 启用 gzip
reportCompressedSize: true
}
}
10. 图片懒加载
<template>
<!-- 使用 loading="lazy" -->
<img :src="url" loading="lazy" alt="image" />
<!-- 或使用指令 -->
<img v-lazy="url" alt="image" />
</template>
关键点
- v-show vs v-if:频繁切换用 v-show,条件稳定用 v-if
- 列表渲染:使用唯一 key,大列表用虚拟滚动
- 减少响应式开销:shallowRef、markRaw 处理大对象
- 组件缓存:KeepAlive 缓存、路由懒加载、异步组件
- 打包优化:代码分割、Tree Shaking、Gzip 压缩
目录