前言
在负责新能源场站智慧管理系统项目时,我们遇到了一个严峻的挑战:随着业务模块不断增加,系统首页加载时间从最初的 2s 飙升到 10s+,用户流失率高达 40%。
经过两周的性能优化攻坚,我们成功将首屏加载时间压缩到 1s 以内,LCP(最大内容绘制)从 8.5s 降至 0.9s,用户留存率提升 35%。
本文将分享我们在 Vue3 项目中实践的性能优化方案,涵盖代码分割、懒加载、构建优化、运行时优化四个维度,希望能给遇到类似问题的同学一些参考。
一、性能问题分析
1.1 现状数据
优化前的性能指标(Chrome DevTools Lighthouse):
| 指标 | 数值 | 评级 |
|---|---|---|
| FCP (首屏内容绘制) | 6.2s | ❌ 差 |
| LCP (最大内容绘制) | 8.5s | ❌ 差 |
| TTI (可交互时间) | 10.3s | ❌ 差 |
| 总包体积 | 4.8MB | ❌ 差 |
| 首屏请求数 | 45 | ❌ 差 |
1.2 问题定位
使用 Webpack Bundle Analyzer 分析打包体积:
npm install --save-dev webpack-bundle-analyzer
// vite.config.js
import { visualizer } from 'rollup-plugin-visualizer'
export default {
plugins: [
visualizer({
open: true,
gzipSize: true,
brotliSize: true,
}),
],
}
发现问题:
-
ECharts 完整包 2.1MB,占总体积 44%
-
组件库全量引入 850KB
-
业务组件未做代码分割
-
图片资源未压缩
二、优化方案
2.1 路由懒加载 + 组件异步加载
优化前:所有路由组件同步加载
// router/index.js - ❌ 不推荐
import Home from '@/views/Home.vue'
import Dashboard from '@/views/Dashboard.vue'
const routes = [
{ path: '/', component: Home },
{ path: '/dashboard', component: Dashboard },
]
优化后:路由级别代码分割
// router/index.js - ✅ 推荐
const routes = [
{
path: '/',
component: () => import('@/views/Home.vue'),
},
{
path: '/dashboard',
component: () => import('@/views/Dashboard.vue'),
},
]
效果:首屏 JS 体积减少 65%,从 2.8MB 降至 980KB
2.2 第三方库按需引入
ECharts 按需加载
优化前:
// ❌ 全量引入 2.1MB
import * as echarts from 'echarts'
优化后:
// ✅ 按需引入,体积减少 80%
import { use } from 'echarts/core'
import { CanvasRenderer } from 'echarts/renderers'
import { BarChart, LineChart, PieChart } from 'echarts/charts'
import {
TitleComponent,
TooltipComponent,
GridComponent,
LegendComponent,
} from 'echarts/components'
use([
CanvasRenderer,
BarChart,
LineChart,
PieChart,
TitleComponent,
TooltipComponent,
GridComponent,
LegendComponent,
])
Element Plus 按需自动导入
npm install -D unplugin-vue-components unplugin-auto-import
// vite.config.js
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
export default {
plugins: [
Components({
resolvers: [ElementPlusResolver()],
}),
],
}
效果:组件库体积从 850KB 降至 180KB
2.3 虚拟列表优化长列表渲染
场景:场站设备列表 5000+ 条数据
优化前:
<template>
<div class="device-list">
<div v-for="device in devices" :key="device.id" class="device-item">
{{ device.name }} - {{ device.status }}
</div>
</div>
</template>
<script setup>
const devices = ref([...5000 条数据])
</script>
优化后 :使用 vue-virtual-scroller
npm install vue-virtual-scroller
<template>
<RecycleScroller
class="device-list"
:items="devices"
:item-size="60"
key-field="id"
>
<template #default="{ item }">
<div class="device-item">
{{ item.name }} - {{ item.status }}
</div>
</template>
</RecycleScroller>
</template>
效果:DOM 节点从 5000+ 降至 20 个,渲染时间从 3.2s 降至 80ms
2.4 图片资源优化
WebP 格式转换
# 使用 imagemin 批量转换
npm install -D vite-plugin-imagemin
// vite.config.js
import viteImagemin from 'vite-plugin-imagemin'
export default {
plugins: [
viteImagemin({
webp: { quality: 80 },
}),
],
}
图片懒加载
<template>
<img v-lazy="device.image" :alt="device.name" />
</template>
<script setup>
import { Lazyload } from 'vue-lazyload'
app.use(Lazyload, {
loading: '/loading.gif',
error: '/error.png',
})
</script>
效果:图片资源体积减少 60%,首屏图片加载时间从 4.5s 降至 1.8s
2.5 Gzip/Brotli 压缩
npm install -D vite-plugin-compression
// vite.config.js
import viteCompression from 'vite-plugin-compression'
export default {
plugins: [
viteCompression({
algorithm: 'brotliCompress',
ext: '.br',
}),
],
}
Nginx 配置:
location / {
gzip on;
gzip_types text/plain application/javascript text/css application/json;
gzip_min_length 1000;
# Brotli
brotli on;
brotli_types text/plain application/javascript text/css application/json;
}
效果:传输体积减少 70%,从 4.8MB 降至 1.4MB
三、优化效果对比
3.1 性能指标提升
| 指标 | 优化前 | 优化后 | 提升幅度 |
|---|---|---|---|
| FCP | 6.2s | 0.8s | 87% ⬆️ |
| LCP | 8.5s | 0.9s | 89% ⬆️ |
| TTI | 10.3s | 1.2s | 88% ⬆️ |
| 总包体积 | 4.8MB | 1.1MB | 77% ⬇️ |
| 首屏请求数 | 45 | 12 | 73% ⬇️ |
3.2 Lighthouse 评分
-
优化前:42 分 ❌
-
优化后:96 分 ✅
3.3 业务指标提升
| 指标 | 优化前 | 优化后 | 变化 |
|---|---|---|---|
| 用户留存率 | 60% | 95% | +35% |
| 页面跳出率 | 45% | 18% | -27% |
| 平均停留时长 | 2.5min | 6.8min | +172% |
四、优化清单总结
✅ 构建时优化
- 路由懒加载
- 组件异步加载
- 第三方库按需引入
- Tree Shaking
- Gzip/Brotli 压缩
- 图片资源压缩
✅ 运行时优化
- 虚拟列表
- 图片懒加载
- 防抖节流
- 请求缓存
- 组件缓存(keep-alive)
✅ 监控与持续优化
- 性能监控埋点
- 错误监控上报
- 定期 Lighthouse 审计
五、核心代码片段
5.1 性能监控埋点
// utils/performance.js
export function reportWebVitals() {
import('web-vitals').then(({ onFCP, onLCP, onCLS, onTTI }) => {
onFCP(console.log)
onLCP(console.log)
onCLS(console.log)
onTTI(console.log)
})
}
// main.js
import { reportWebVitals } from '@/utils/performance'
reportWebVitals()
5.2 请求拦截优化
// utils/request.js
import axios from 'axios'
const request = axios.create({
baseURL: import.meta.env.VITE_API_BASE_URL,
timeout: 10000,
})
// 请求缓存
const cache = new Map()
request.interceptors.request.use((config) => {
if (config.cache) {
const cached = cache.get(config.url)
if (cached) {
return cached
}
}
return config
})
export default request
六、总结
性能优化是一个持续迭代的过程,需要根据实际业务场景选择合适的方案。本次优化的核心思路:
-
减少体积:代码分割、按需引入、压缩
-
减少请求:缓存策略、资源合并
-
优化渲染:虚拟列表、懒加载
-
持续监控:埋点统计、定期审计
核心指标:首屏加载 < 1s,LCP < 2.5s,TTI < 3s
希望本文能对你有所帮助!如有疑问,欢迎在评论区交流~
参考资料
如果觉得有帮助,欢迎点赞 👍 + 收藏 ⭐ + 关注,后续会分享更多前端实战经验!