Vue 项目首屏加载从 5s 到 1.5s:4 步落地优化方案,附完整代码 + 数据对比
前段时间我在做一个活动时,打包加载后发现打开页面要等半天,经过几天的优化,最终将首屏加载时间从5秒压到 1.5 秒。
这篇文章会把整个优化过程拆解成「文件分析→代码解决→验证」三步,每个方案都附完整代码和前后数据对比,新手也能直接复用。
1. 打包体积分析:看「哪些文件在拖后腿」
操作步骤(Vue CLI/Vite 通用):
- Vue CLI 项目:执行 npm run build -- --report;
- Vite 项目:执行 npm run build -- --report(需先在 vite.config.js 中开启 build.report: true);
- 打包完成后,dist 目录会生成 report.html,打开后能看到「打包体积占比图」。
我的项目诊断结果:
问题类型 |
具体表现 |
占比 |
路由未懒加载 |
所有路由组件打包进 app.js,导致初始 JS 体积过大 |
35% |
UI 库完整引入 |
Element Plus 完整引入(600KB),但仅用 5 个组件 |
30% |
图片未优化 |
banner 图未压缩(2.3MB),小图标未转 Base64 |
25% |
公共代码未拆分 |
Vue、lodash 等第三方库重复打包 |
10% |
明确了这 4 个核心问题,接下来的优化就有了方向 —— 不用贪多,解决这 4 个问题,首屏加载就能提速 60% 以上。
2. 4种方法可以优化:每个方案都附代码 + 效果
方案 1:路由懒加载
原理:
默认情况下,Vue 会把所有路由组件打包进「app.js」,导致初始加载时要下载全部组件;懒加载则是「用户访问哪个路由,才下载哪个组件的 JS」,能直接减少初始 JS 体积。
代码对比:
// 优化前:同步加载(所有组件打包进app.js)
import Home from './views/Home.vue'
import About from './views/About.vue'
const routes = [
{ path: '/', component: Home },
{ path: '/about', component: About }
]
// 优化后:懒加载(按需下载)
const routes = [
// 动态import语法,访问时才加载
{ path: '/', component: () => import('./views/Home.vue') },
{ path: '/about', component: () => import('./views/About.vue') }
]
效果对比:
指标 |
优化前 |
优化后 |
提升 |
初始 JS 体积 |
1.8MB |
1.08MB |
40% |
首屏加载时间 |
5.0s |
3.2s |
36% |
避坑提示:
- 不要给「首屏路由」(如 Home)做懒加载,否则会增加首屏加载的请求数;
方案 2:UI 库按需引入(减少 200KB,自动生效)
原理:
完整引入 UI 库(如 Element Plus)会包含大量未使用的组件和样式(比如你只用了按钮,却加载了日历、表格等组件),按需引入能只打包「实际使用的组件」。
以 Element Plus 为例(Vite 项目):
- 安装按需引入插件:
npm install unplugin-vue-components unplugin-auto-import -D
- 在 vite.config.js 中配置:
import { defineConfig } from 'vite'
import Vue from '@vitejs/plugin-vue'
// 1. 引入按需引入插件
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(),
// 2. 配置插件
AutoImport({
resolvers: [ElementPlusResolver()], // 自动引入Element Plus的API(如ElMessage)
}),
Components({
resolvers: [ElementPlusResolver()], // 自动引入Element Plus的组件(如ElButton)
}),
],
})
- 在组件中直接使用,无需手动 import:
<template>
<!-- 无需引入ElButton,插件会自动处理 -->
<el-button type="primary" @click="showMessage">提交</el-button>
</template>
<script setup>
// 无需引入ElMessage,插件会自动引入
const showMessage = () => {
ElMessage.success('提交成功')
}
</script>
效果对比:
指标 |
优化前 |
优化后 |
提升 |
Element Plus 体积 |
600KB |
400KB |
33% |
首屏加载时间 |
3.2s |
2.8s |
12.5% |
避坑提示:
- 如果使用 Vue CLI,配置方式略有不同(需在 vue.config.js 中配置);
- 部分 UI 库的「样式」需要单独按需引入(如 Ant Design Vue),需查看官方文档。
方案 3:图片优化(首屏资源减少 50%,用户感知最明显)
图片是首屏加载的「重灾区」—— 我的项目中,图片占了首屏资源体积的 50%,优化后直接减少了 1.6MB。
推荐 3 个必做的图片优化手段,覆盖 90% 的场景:
1. 小图转 Base64(减少 HTTP 请求)
- 原理:小于 10KB 的图片(如图标、小按钮)转为 Base64 后,会嵌入 HTML/JS 中,无需额外发请求;
- 实现(Vite 自动处理):
// vite.config.js
export default defineConfig({
build: {
assetsInlineLimit: 10240, // 10KB以下图片自动转Base64(默认是4KB)
},
})
2. 大图用 WebP 格式(体积减少 50%)
- 原理:WebP 格式比 JPG/PNG 体积小 30%~50%,且支持透明和动图;
- 实现:
- 用工具将图片转为 WebP(推荐 TinyPNG、Squoosh);
- 在 Vue 中使用,同时兼容不支持 WebP 的浏览器:
<template>
<!-- 优先加载WebP,不支持则加载JPG -->
<picture>
<source srcset="/banner.webp" type="image/webp">
<img src="/banner.jpg" alt="首页banner" class="banner">
</picture>
</template>
3. 首屏外图片懒加载(延迟加载,减少首屏压力)
- 原理:首屏外的图片(如下方的商品列表),等用户滚动到可视区域再加载;
- 实现(用 vue-lazyload):
- 安装插件:
npm install vue-lazyload
- 在 main.js 中配置:
import { createApp } from 'vue'
import App from './App.vue'
import VueLazyload from 'vue-lazyload'
const app = createApp(App)
// 配置懒加载:加载中显示占位图
app.use(VueLazyload, {
loading: '/loading.gif', // 加载中占位图(建议小于1KB)
error: '/error.png' // 加载失败占位图
})
app.mount('#app')
- 在组件中使用:
<template>
<!-- 用v-lazy替代src -->
<img v-lazy="item.imgUrl" alt="商品图片" class="product-img" v-for="item in productList" :key="item.id">
</template>
效果对比:
指标 |
优化前 |
优化后 |
提升 |
首屏图片体积 |
2.3MB |
1.1MB |
52% |
首屏加载时间 |
2.8s |
2.0s |
28.6% |
避坑提示:
- 首屏图片不要懒加载,否则会导致首屏空白;
- WebP 格式在 IE 浏览器不支持,需用 <picture> 标签做兼容。
方案 4:打包优化(最后一步,再挤 10% 体积)
前面 3 个方案解决了核心问题,最后通过打包优化,进一步压缩体积。
1. 拆分公共代码(避免重复打包)
- 原理:Vue、lodash 等第三方库,会被多个组件引用,拆分后只打包一次,后续组件复用;
- 实现(Vite 配置):
// vite.config.js
export default defineConfig({
build: {
rollupOptions: {
output: {
// 手动拆分公共代码
manualChunks: {
vue: ['vue'], // Vue相关代码拆成一个chunk
lodash: ['lodash'], // lodash拆成一个chunk
element: ['element-plus'] // Element Plus拆成一个chunk
}
}
}
}
})
2. JS/CSS 压缩(默认开启,无需额外配置)
- Vite 默认使用 Terser 压缩 JS,CSSNano 压缩 CSS,无需手动配置;
- 若需加强压缩(如去掉 console.log),可在 vite.config.js 中配置:
export default defineConfig({
build: {
terserOptions: {
compress: {
drop_console: true, // 打包时去掉所有console.log
drop_debugger: true // 去掉debugger
}
}
}
})
效果对比:
指标 |
优化前 |
优化后 |
提升 |
总打包体积 |
2.5MB |
1.0MB |
60% |
首屏加载时间 |
2.0s |
1.5s |
25% |