Vue3 性能优化
编译时优化
Vue3 在编译阶段做了大量优化:
- 静态提升
- 预字符串化
- 缓存事件处理函数
- Block Tree
- PatchFlag
代码层面优化
v-once
<template>
<div v-once>
<h1>{{ title }}</h1>
<p>{{ content }}</p>
</div>
</template>
v-memo
<template>
<div v-memo="[valueA, valueB]">
<!-- 只有 valueA 或 valueB 改变时才更新 -->
</div>
</template>
计算属性缓存
<script setup>
import { ref, computed } from 'vue'
const list = ref([...])
// 好 - 使用计算属性
const filteredList = computed(() => {
return list.value.filter(item => item.active)
})
// 差 - 使用方法
const getFilteredList = () => {
return list.value.filter(item => item.active)
}
</script>
shallowRef / shallowReactive
<script setup>
import { shallowRef, shallowReactive } from 'vue'
// 只有根级别是响应式的
const state = shallowReactive({
foo: 1,
nested: { bar: 2 }
})
// 大型不可变数据结构
const bigData = shallowRef({
// 大量数据
})
</script>
虚拟列表
<script setup>
import { ref } from 'vue'
import { useVirtualList } from '@vueuse/core'
const allItems = ref(Array.from({ length: 10000 }, (_, i) => i))
const { list, containerProps, wrapperProps } = useVirtualList(
allItems,
{ itemHeight: 50 }
)
</script>
<template>
<div v-bind="containerProps" style="height: 400px">
<div v-bind="wrapperProps">
<div v-for="item in list" :key="item.index">
{{ item.data }}
</div>
</div>
</div>
</template>
异步组件
<script setup>
import { defineAsyncComponent } from 'vue'
const HeavyComponent = defineAsyncComponent(() =>
import('./HeavyComponent.vue')
)
</script>
KeepAlive
<template>
<KeepAlive :max="10">
<component :is="currentComponent"></component>
</KeepAlive>
</template>
Tree-shaking
// 只导入需要的功能
import { ref, computed } from 'vue'
// 而不是
import * as Vue from 'vue'
打包优化
代码分割
// vite.config.js
export default {
build: {
rollupOptions: {
output: {
manualChunks: {
'vue-vendor': ['vue', 'vue-router', 'pinia'],
'ui-vendor': ['element-plus']
}
}
}
}
}
压缩
// vite.config.js
import { defineConfig } from 'vite'
import viteCompression from 'vite-plugin-compression'
export default defineConfig({
plugins: [
viteCompression({
algorithm: 'gzip',
ext: '.gz'
})
]
})
CDN
// vite.config.js
export default {
build: {
rollupOptions: {
external: ['vue', 'vue-router'],
output: {
globals: {
vue: 'Vue',
'vue-router': 'VueRouter'
}
}
}
}
}
图片优化
懒加载
<script setup>
import { useIntersectionObserver } from '@vueuse/core'
import { ref } from 'vue'
const target = ref(null)
const isVisible = ref(false)
useIntersectionObserver(target, ([{ isIntersecting }]) => {
if (isIntersecting) {
isVisible.value = true
}
})
</script>
<template>
<img
ref="target"
:src="isVisible ? actualSrc : placeholderSrc"
/>
</template>
WebP
<template>
<picture>
<source srcset="image.webp" type="image/webp">
<img src="image.jpg" alt="Image">
</picture>
</template>
性能监控
<script setup>
import { onMounted } from 'vue'
onMounted(() => {
// Performance API
const perfData = performance.getEntriesByType('navigation')[0]
console.log('DOM Content Loaded:', perfData.domContentLoadedEventEnd)
console.log('Load Complete:', perfData.loadEventEnd)
})
</script>