Vue3项目上线后首屏白屏时间长,多数原因在于主包体积过大。生产环境的优化往往围绕”加载时机”与”加载体积”两个方向:把不首屏需要的代码延后加载,把大体积依赖拆出去。本文给出可直接套用的几类方案:路由懒加载、组件异步化、依赖分包和CDN缓存策略。
先量化再动手:打开构建产物分析
# vite.config.ts 中启用分析
import { visualizer } from 'rollup-plugin-visualizer'
plugins: [visualizer({ gzipSize: true, open: true })]
构建后浏览器会自动打开产物分析图,优先看最大块(chunk)由哪些模块组成。判断主包过大时,先看最大的chunk是否有业务代码,还是第三方依赖。常见情况:整个UI组件库全量打进主包,而实际用到不足三分之一。
路由级懒加载:Vue Router按需分包
const routes = [
{
path: '/dashboard',
component: () => import('@/views/Dashboard.vue'),
meta: { title: '工作台' }
},
{
path: '/reports',
component: () => import('@/views/Reports.vue'),
},
]
使用import()替换静态引入后,构建工具会自动将每个路由生成独立chunk,只有访问对应页面时才下载代码。注意:页面级组件用懒加载后,首次访问单个页面仍要加载该页全部依赖,所以对报表这类带大图表的页面,可再做组件级异步化。
组件级异步化:异步组件与defineAsyncComponent
页面中某个区域(图表、富文本、地图)体积大且非首屏必要内容,用异步组件只在该区域渲染时才加载:
import { defineAsyncComponent } from 'vue'
const ChartPanel = defineAsyncComponent(() =>
import('@/components/ChartPanel.vue'),
)
// 模板中使用 <Suspense> 包裹;Vue3.3+ 可直接写
加载期间可传入loadingComponent与errorComponent,避免区域空白。使用场景判断:首屏可见性低于50%且体积大于50KB的组件,优先异步化。
构建分包:manualChunks与vendor缓存
build: {
rollupOptions: {
output: {
manualChunks(id) {
if (id.includes('node_modules')) {
if (id.includes('echarts') || id.includes('lodash')) {
return 'charts-vendor'
}
if (id.includes('vue') || id.includes('pinia') || id.includes('vue-router')) {
return 'vue-core'
}
return 'vendor'
}
},
}
}
}
分包的核心收益不是变小,而是缓存复用:核心框架vendor稳定后长期命中缓存,业务包更新时不用重新下载依赖。配合CDN,可以把echarts、dayjs这类大依赖改为外链引入,进一步削减包体。
图片与字体处理:经常被忽略的体积大头
// 图片启用压缩与宽高属性
images: { formats: ['avif', 'webp'] }
// 字体用font-display: swap + 子集化
统计显示很多项目首屏加载流量的40%以上来自图片与字体。PNG大图转为AVIF/WebP、小图标合入SVG雪碧图、字体按用到的字符做子集化,这些改动对LCP的提升往往比调分包更直接。
验证优化效果:关键指标对比
优化完跑Lighthouse或Chrome DevTools的Performance面板,记录三项指标:FCP(首屏内容绘制)、LCP(最大内容绘制)、TTI(可交互时间)。优化前的主包体积、优化后对比,以及首屏是否先渲染出框架骨架再补内容,这三项要有前后对照数据才说明改动有效。实践中有项目仅做路由懒加载+图片压缩,LCP从4.2秒降到1.8秒,说明这两项改动在同场景下收益最大。
原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/vue3-xiang-mu-shou-ping-jia-zai-you-hua-lu-you-lan-jia-zai/