Vue3 作为目前主流的前端框架,其性能优化涉及代码层面、构建层面、运行时层面等多个维度。
一、代码层面优化(最易落地)
1. 响应式优化:精准控制响应式数据
Vue3 的 reactive/ref 会对数据做深度响应式处理,但很多场景下我们不需要全量响应式:
- 场景 1:纯展示数据(如接口返回的列表、详情),无需响应式
- 场景 2:大型对象仅部分属性需要响应式
解决方案:
xml
<template>
<div>{{ staticData.name }}</div>
<div>{{ reactiveData.age }}</div>
</template>
<script setup>
import { reactive, markRaw, toRefs } from 'vue'
// 1. 纯静态数据:用 markRaw 跳过响应式转换(减少 Proxy 开销)
const staticData = markRaw({
name: 'Vue3 性能优化',
desc: '这是纯展示数据,无需响应式'
})
// 2. 大型对象仅部分属性响应式:用 toRefs 只代理需要的属性
const rawData = {
name: '张三',
age: 20,
address: '北京',
// 100+ 其他属性...
}
const reactiveData = reactive({
age: toRefs(rawData).age // 仅 age 响应式
})
</script>
2. 模板渲染优化:减少不必要的重渲染
Vue3 默认会在组件依赖的响应式数据变化时重渲染,但很多时候我们可以精准控制:
- 方案 1 :使用
v-memo缓存模板片段(Vue3.2+ 支持) - 方案 2 :组件拆分 +
defineProps精准接收 props
示例 1:v-memo 缓存列表项
xml
<template>
<!-- 仅当 item.id 或 item.status 变化时,才重新渲染该列表项 -->
<div v-for="item in list" :key="item.id" v-memo="[item.id, item.status]">
<div>{{ item.name }}</div>
<div>{{ item.status }}</div>
</div>
</template>
示例 2:组件拆分 + 精准 props
xml
<!-- 父组件 Parent.vue -->
<template>
<Child :age="user.age" /> <!-- 仅传递需要的属性,而非整个 user 对象 -->
</template>
<!-- 子组件 Child.vue -->
<script setup>
// 仅接收需要的 props,减少重渲染触发条件
const props = defineProps({
age: {
type: Number,
required: true
}
})
</script>
3. 计算属性 / 侦听器优化
- 计算属性:利用缓存特性(依赖不变则不重新计算),替代频繁执行的方法
- 侦听器 :使用
watch的immediate: false(默认)+deep: false(默认),避免无意义的深度监听
xml
<script setup>
import { ref, computed, watch } from 'vue'
const list = ref([1, 2, 3, 4])
// 推荐:计算属性(缓存结果)
const total = computed(() => {
return list.value.reduce((sum, item) => sum + item, 0)
})
// 不推荐:方法(每次渲染都执行)
const getTotal = () => {
return list.value.reduce((sum, item) => sum + item, 0)
}
// 侦听器优化:仅监听需要的属性,关闭深度监听
const user = ref({ name: '张三', age: 20 })
watch(
() => user.value.age, // 仅监听 age 属性
(newVal) => {
console.log('年龄变化:', newVal)
},
{ deep: false } // 默认 false,无需手动写,此处仅强调
)
</script>
二、构建层面优化(提升打包速度 + 减小体积)
1. 按需引入第三方库(如 Element Plus)
默认全量引入会导致打包体积暴增,需配置按需引入:
步骤 1:安装插件
arduino
npm install unplugin-auto-import unplugin-vue-components -D
步骤 2 :修改 vite.config.js(Vite 项目)
javascript
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
export default defineConfig({
plugins: [
vue(),
// 自动导入 Vue 相关 API
AutoImport({
resolvers: [ElementPlusResolver()]
}),
// 自动导入组件(按需引入 Element Plus)
Components({
resolvers: [ElementPlusResolver()]
})
]
})
2. 开启 Vite 构建压缩(减小打包体积)
javascript
// vite.config.js
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { compress } from 'vite-plugin-compression'
export default defineConfig({
plugins: [
vue(),
// 开启 gzip 压缩
compress({
ext: '.gz',
algorithm: 'gzip',
threshold: 10240 // 大于 10kb 的文件才压缩
})
],
build: {
// 开启代码分割
rollupOptions: {
output: {
manualChunks(id) {
// 将 node_modules 中的代码拆分为单独的 chunk
if (id.includes('node_modules')) {
return id.toString().split('node_modules/')[1].split('/')[0].toString()
}
}
}
},
// 移除 console 和 debugger(生产环境)
minify: 'terser',
terserOptions: {
compress: {
drop_console: true,
drop_debugger: true
}
}
}
})
3. 图片 / 资源优化
- 使用
vite-plugin-imagemin压缩图片 - 小图片转 base64(减少 HTTP 请求)
php
// vite.config.js
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import imagemin from 'vite-plugin-imagemin'
export default defineConfig({
plugins: [
vue(),
imagemin({
gifsicle: { optimizationLevel: 7 },
optipng: { optimizationLevel: 7 },
mozjpeg: { quality: 80 },
pngquant: { quality: [0.8, 0.9], speed: 4 },
svgo: { plugins: [{ name: 'removeViewBox' }] }
})
],
build: {
assetsInlineLimit: 4096 // 小于 4kb 的资源转 base64
}
})
三、运行时优化(提升用户体验)
1. 路由懒加载(减少首屏加载时间)
Vue3 结合 Vue Router 4 实现路由懒加载,核心是 import() 动态导入:
javascript
// router/index.js
import { createRouter, createWebHistory } from 'vue-router'
const routes = [
{
path: '/',
name: 'Home',
// 懒加载:仅访问该路由时才加载组件
component: () => import('../views/Home.vue')
},
{
path: '/detail',
name: 'Detail',
// 带分包命名:方便打包后分析体积
component: () => import(/* webpackChunkName: "detail" */ '../views/Detail.vue')
}
]
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes
})
export default router
2. 虚拟列表(优化长列表渲染)
当列表数据超过 1000 条时,直接渲染会导致 DOM 节点过多、卡顿,需用虚拟列表:
步骤 1 :安装第三方库(推荐 vue-virtual-scroller)
arduino
npm install vue-virtual-scroller
步骤 2:使用虚拟列表组件
xml
<template>
<!-- 虚拟列表:仅渲染可视区域的 DOM 节点 -->
<RecycleScroller
class="scroller"
:items="longList"
:item-size="50" // 每个列表项的高度(像素)
key-field="id"
v-slot="{ item }"
>
<div class="list-item">{{ item.name }}</div>
</RecycleScroller>
</template>
<script setup>
import { RecycleScroller } from 'vue-virtual-scroller'
import 'vue-virtual-scroller/dist/vue-virtual-scroller.css'
import { ref } from 'vue'
// 模拟 10 万条长列表数据
const longList = ref([])
for (let i = 0; i < 100000; i++) {
longList.value.push({ id: i, name: `列表项 ${i}` })
}
</script>
<style scoped>
.scroller {
height: 500px; /* 固定容器高度 */
overflow: auto;
}
.list-item {
height: 50px;
line-height: 50px;
border-bottom: 1px solid #eee;
}
</style>
3. 避免同步阻塞主线程
耗时操作(如大数据处理、复杂计算)放到 nextTick 或 Web Worker 中执行:
xml
<script setup>
import { nextTick, ref } from 'vue'
const data = ref([])
// 模拟耗时操作
const handleBigData = async () => {
// 先展示加载状态,避免页面卡顿
const loading = ref(true)
// 放到 nextTick 中执行,不阻塞当前渲染
await nextTick()
// 复杂计算(如果更耗时,建议用 Web Worker)
const result = []
for (let i = 0; i < 1000000; i++) {
result.push(i * 2)
}
data.value = result
loading.value = false
}
</script>
四、性能监控(验证优化效果)
优化后需要验证效果,推荐使用 Vue 官方的 vue-devtools 或浏览器 DevTools:
- Vue DevTools:查看组件重渲染次数、响应式数据依赖
- 浏览器 Performance 面板:录制页面加载 / 交互过程,分析卡顿点
- 打包体积分析 :Vite 项目可使用
rollup-plugin-visualizer:
arduino
# 安装体积分析插件
npm install rollup-plugin-visualizer -D
# 修改 vite.config.js
import { defineConfig } from 'vite'
import { visualizer } from 'rollup-plugin-visualizer'
export default defineConfig({
plugins: [
// 其他插件...
visualizer({
open: true, // 打包后自动打开分析页面
filename: 'stats.html' // 生成的分析文件
})
]
})
总结
Vue3 项目性能优化的核心关键点:
- 代码层面 :精准控制响应式数据(
markRaw/toRefs)、用v-memo减少重渲染、计算属性替代频繁执行的方法; - 构建层面:按需引入第三方库、开启 gzip 压缩、拆分代码块、压缩图片资源;
- 运行时层面:路由懒加载减少首屏加载时间、虚拟列表优化长列表渲染、避免同步阻塞主线程。
所有优化手段都需按需使用,优先解决用户感知最明显的性能问题(如首屏加载慢、列表滚动卡顿),避免过度优化。