首屏转了三秒才出来?给路由做懒加载把包拆开
一、背景:打包完首屏白等三秒
之前做的一个 Vue3 后台管理系统,菜单挂了二十多个页面。刚开始为了省事,所有视图都在入口 main.ts 里静态 import 进来,Rollup 把它们全打进同一个 app.js。打包完一看,app.js 有 1.2MB(gzip 之后还有 380KB 左右)。用户登录后点进首页,要白屏转三秒多才出内容,弱网环境更夸张,体验很差。问题就出在:用户只想看一个首页,浏览器却把二十多个页面的代码全下载执行了一遍。当时运营侧也收到过用户反馈说后台点开很卡,但后端接口监控显示响应并不慢,瓶颈其实全卡在前端打包体积上,属于典型的前端性能问题,这锅后端不背。
二、路由懒加载:用到哪个页面才加载哪个
最直接的改法,是把静态导入换成动态导入。路由表里原来写 component: OrderView,改成 component: () => import('../views/Order.vue')。Vite 底层是 Rollup,看到这种动态 import 会自动把每个视图拆成独立的 chunk 文件。用户访问哪个路由,才去下载那个路由对应的 chunk,没访问的页面代码根本不进首屏。首页只加载登录态和首页本身那一两个 chunk,首屏 JS 直接从 1.2MB 掉到 400KB 上下,白屏时间压到了 1 秒以内。改完打包,dist 里会多出一堆 Order-xxxx.js 这样的文件,正是按路由拆出来的独立 chunk。
// router/index.ts
import { createRouter, createWebHistory } from 'vue-router'
const routes = [
{ path: '/', component: () => import('../views/Home.vue') },
{ path: '/order', component: () => import('../views/Order.vue') },
{ path: '/report', component: () => import('../views/Report.vue') },
]
export default createRouter({ history: createWebHistory(), routes })
三、再拆公共 vendor:第三方库单独缓存
光做路由懒加载还不够。vue、vue-router、pinia、element-plus、echarts 这些第三方库,默认会进公共 chunk,每次刷新首屏还是得下它们。解决办法是在 vite.config.ts 里用 build.rollupOptions.output.manualChunks,把体积大的库单独拆出来:
// vite.config.ts
export default defineConfig({
base: '/admin/',
build: {
rollupOptions: {
output: {
manualChunks: {
echarts: ['echarts'],
vendor: ['vue', 'vue-router', 'pinia', 'element-plus'],
},
},
},
},
})
拆完之后,vendor 和 echarts 这种不常变的库能走浏览器长缓存,业务代码发版更新时它们命中缓存不用重新下,首屏又能再快一截。
四、关键点
第一,路由部署在子路径(比如整个后台挂在 https://域名/admin/ 下)时,动态 import 的 chunk 请求默认是相对当前页面路径的。用户直接刷新 /admin/order 子路由,chunk 的相对路径会算错,结果 404。修法是给 vite.config.ts 配 base: '/admin/',或者用 import.meta.env.BASE_URL 拼绝对路径,保证 chunk 永远走正确的绝对地址。
第二,echarts 一开始是 import * as echarts from 'echarts' 全量引入,光这一个库就吃了将近 1MB 打进 vendor。后来改成按需引入,只注册用到的图表和组件,vendor 又小了一半:
import * as echarts from 'echarts/core'
import { LineChart, BarChart } from 'echarts/charts'
import { GridComponent, TooltipComponent } from 'echarts/components'
import { CanvasRenderer } from 'echarts/renderers'
echarts.use([LineChart, BarChart, GridComponent, TooltipComponent, CanvasRenderer])
第三,懒加载之后首屏请求数会变多(可能十几个小 chunk),在 HTTP/1.1 下受并发数限制反而可能更慢;要么上 HTTP/2,要么把极小的 chunk 通过 build.assetsInlineLimit 内联进主文件,缓解请求数。
五、怎么验证真的瘦了
打包完别凭感觉,看两处:一是 dist/assets 目录,确认每个路由都对应一个独立 chunk 文件;二是浏览器开 DevTools → Network,勾上「Disable cache」刷新首页,看实际下载的 JS 总大小。想更直观,可以装 rollup-plugin-visualizer,npm run build 后生成一张体积构成图,哪个包最大一目了然,缺哪块优化很清楚。
六、文章总结
路由懒加载加 manualChunks 分包,是前端首屏优化里性价比最高的一步——不改业务逻辑、纯配置,白屏就能从三秒压到一秒内。再叠上 CDN 和 gzip/brotli 压缩,基本就把首屏体量的问题解决了。这也是我后来每个项目初始化必做的一步。后来顺手用 Lighthouse 跑了下首屏评分,从 40 多分涨到 80 分以上,算是配置优化顺手拿到的副产品。
- 点赞
- 收藏
- 关注作者
评论(0)