Vue 项目优化实践
Vue 项目中常用的性能优化方法和代码示例
问题
在实际工作中,你对 Vue 做过哪些优化?
解答
1. 代码层面优化
v-if 与 v-show 的选择
<template>
<!-- 频繁切换用 v-show,只是 CSS 切换 -->
<div v-show="isVisible">频繁切换的内容</div>
<!-- 条件很少改变用 v-if,真正的条件渲染 -->
<HeavyComponent v-if="isLoaded" />
</template>
v-for 使用唯一 key
<template>
<!-- 使用唯一标识作为 key,避免使用 index -->
<div v-for="item in list" :key="item.id">
{{ item.name }}
</div>
</template>
computed 缓存计算结果
<script setup>
import { ref, computed } from 'vue'
const list = ref([1, 2, 3, 4, 5])
// computed 会缓存结果,依赖不变时不会重新计算
const total = computed(() => {
console.log('计算 total')
return list.value.reduce((sum, n) => sum + n, 0)
})
</script>
使用 keep-alive 缓存组件
<template>
<!-- 缓存组件状态,避免重复渲染 -->
<keep-alive :include="['Home', 'List']" :max="10">
<router-view />
</keep-alive>
</template>
2. 路由懒加载
// router/index.js
const routes = [
{
path: '/home',
// 动态导入,按需加载
component: () => import('@/views/Home.vue')
},
{
path: '/about',
// 指定 chunk 名称,便于调试
component: () => import(/* webpackChunkName: "about" */ '@/views/About.vue')
}
]
3. 组件异步加载
<script setup>
import { defineAsyncComponent } from 'vue'
// 异步加载重型组件
const HeavyChart = defineAsyncComponent({
loader: () => import('./HeavyChart.vue'),
loadingComponent: LoadingSpinner,
delay: 200,
timeout: 3000
})
</script>
4. 虚拟滚动处理长列表
<template>
<!-- 使用 vue-virtual-scroller 处理大数据列表 -->
<RecycleScroller
:items="bigList"
: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'
// 假设有 10000 条数据
const bigList = ref([...])
</script>
5. 图片懒加载
<template>
<!-- 使用 loading="lazy" 原生懒加载 -->
<img
v-for="img in images"
:key="img.id"
:src="img.url"
loading="lazy"
alt=""
/>
</template>
<!-- 或使用 v-lazy 指令 -->
<template>
<img v-lazy="img.url" />
</template>
6. 防抖节流
<script setup>
import { useDebounceFn, useThrottleFn } from '@vueuse/core'
// 搜索输入防抖
const handleSearch = useDebounceFn((keyword) => {
fetchSearchResult(keyword)
}, 300)
// 滚动事件节流
const handleScroll = useThrottleFn(() => {
checkScrollPosition()
}, 100)
</script>
<template>
<input @input="e => handleSearch(e.target.value)" />
<div @scroll="handleScroll">...</div>
</template>
7. 打包优化
// vite.config.js
import { defineConfig } from 'vite'
export default defineConfig({
build: {
// 分包策略
rollupOptions: {
output: {
manualChunks: {
// 第三方库单独打包
vendor: ['vue', 'vue-router', 'pinia'],
// UI 库单独打包
ui: ['element-plus']
}
}
},
// 启用 gzip 压缩
reportCompressedSize: true
}
})
8. 使用 shallowRef/shallowReactive
<script setup>
import { shallowRef, shallowReactive } from 'vue'
// 大对象只需要浅层响应式
const bigData = shallowRef({
// 大量嵌套数据...
})
// 更新时整体替换
bigData.value = { ...newData }
</script>
关键点
- v-if/v-show:频繁切换用 v-show,条件稳定用 v-if
- 路由懒加载:使用动态 import 按需加载页面组件
- 虚拟滚动:长列表使用虚拟滚动,只渲染可视区域
- keep-alive:缓存不活跃组件,避免重复渲染
- 合理使用响应式:大数据用 shallowRef,避免深层响应式开销
目录