Vue3项目性能优化

18 阅读5分钟

Vue3 作为目前主流的前端框架,其性能优化涉及代码层面、构建层面、运行时层面等多个维度。

一、代码层面优化(最易落地)

1. 响应式优化:精准控制响应式数据

Vue3 的 reactive/ref 会对数据做深度响应式处理,但很多场景下我们不需要全量响应式:

  • 场景 1:纯展示数据(如接口返回的列表、详情),无需响应式
  • 场景 2:大型对象仅部分属性需要响应式

解决方案

<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 缓存列表项

<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

<!-- 父组件 Parent.vue -->
<template>
  <Child :age="user.age" /> <!-- 仅传递需要的属性,而非整个 user 对象 -->
</template>

<!-- 子组件 Child.vue -->
<script setup>
// 仅接收需要的 props,减少重渲染触发条件
const props = defineProps({
  age: {
    type: Number,
    required: true
  }
})
</script>

3. 计算属性 / 侦听器优化

  • 计算属性:利用缓存特性(依赖不变则不重新计算),替代频繁执行的方法
  • 侦听器:使用 watchimmediate: false(默认)+ deep: false(默认),避免无意义的深度监听
<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:安装插件

npm install unplugin-auto-import unplugin-vue-components -D

步骤 2:修改 vite.config.js(Vite 项目)

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 构建压缩(减小打包体积)

// 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 请求)
// 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() 动态导入:

// 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

npm install vue-virtual-scroller

步骤 2:使用虚拟列表组件

<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 中执行:

<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:

  1. Vue DevTools:查看组件重渲染次数、响应式数据依赖
  2. 浏览器 Performance 面板:录制页面加载 / 交互过程,分析卡顿点
  3. 打包体积分析:Vite 项目可使用 rollup-plugin-visualizer
# 安装体积分析插件
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 项目性能优化的核心关键点:

  1. 代码层面:精准控制响应式数据(markRaw/toRefs)、用 v-memo 减少重渲染、计算属性替代频繁执行的方法;
  2. 构建层面:按需引入第三方库、开启 gzip 压缩、拆分代码块、压缩图片资源;
  3. 运行时层面:路由懒加载减少首屏加载时间、虚拟列表优化长列表渲染、避免同步阻塞主线程。

所有优化手段都需按需使用,优先解决用户感知最明显的性能问题(如首屏加载慢、列表滚动卡顿),避免过度优化。