Vite 打包后首屏加载太慢,怎么优化?

长孙培培 阅读 55

我用 Vite + Vue3 开发了一个项目,本地开发时很快,但 build 之后首屏加载特别慢,Lighthouse 评分很低。我已经试过开启 brotli 压缩和分包,但效果不明显。

是不是我的组件没做懒加载?比如下面这个路由写法是不是有问题?

<script setup>
import { createRouter, createWebHistory } from 'vue-router'
const routes = [
  {
    path: '/dashboard',
    component: () => import('@/views/Dashboard.vue')
  }
]
const router = createRouter({ history: createWebHistory(), routes })
</script>
我来解答 赞 8 收藏
二维码
手机扫码查看
2 条解答
A. 慧娇
A. 慧娇 Lv1
先看一下你的路由写法,动态导入 () => import('@/views/Dashboard.vue') 这个其实是对的,路由懒加载没问题。

但首屏加载慢的原因多了去了,路由懒加载只是其中一环。让我帮你理一理:

先检查打包产物到底有多大

在你的项目根目录运行 npm run build,然后看 dist 文件夹里每个 chunk 的大小。你可以用 npm run preview 跑起来,然后用 Chrome DevTools 的 Network 面板看具体哪个文件慢。

几个常见的坑和解决办法

1. 第三方库没分离出来

你的 vite.config.ts 大概需要这样配置:

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { resolve } from 'path'

export default defineConfig({
plugins: [vue()],
build: {
rollupOptions: {
output: {
manualChunks: {
// 把 vue、vue-router、pinia 这些核心库单独打一个 chunk
'vendor-vue': ['vue', 'vue-router', 'pinia'],
// 把其他第三方库打包到一起
'vendor-lib': ['lodash', 'axios', 'dayjs']
}
}
},
// 启用 gzip 压缩
minify: 'terser',
terserOptions: {
compress: {
drop_console: true,
drop_debugger: true
}
}
}
})


这样做的原理是把不常变化的第三方库和业务代码分开,浏览器可以长期缓存 vendor chunk。

2. 首屏路由的 chunk 太大

如果你的 Dashboard.vue 本身依赖了很多其他模块,即使路由懒加载了,加载 Dashboard 的时候还是会下载一堆东西。可以这样优化:

// 把 Dashboard 内部的大组件也改成异步导入
// 在 Dashboard.vue 里
import { defineComponent, defineAsyncComponent } from 'vue'

// 大组件用异步组件
const HeavyChart = defineAsyncComponent(() =>
import('./components/HeavyChart.vue')
)

export default defineComponent({
components: {
HeavyChart
}
})


3. 首屏 CSS 没内联

Vite 默认会把 CSS 抽成单独文件,首屏需要等 CSS 加载完才能渲染。可以改成内联:

export default defineConfig({
build: {
cssCodeSplit: false, // 关闭 CSS 代码分割,全部内联
// 或者用这个配置让首屏 CSS 内联
cssInjectedByJs: true
}
})


4. 资源预加载

首屏路由加载完之后,可以预加载其他可能访问的路由。用 vite 的 plugin 可以实现:

// 安装 npm i -D @vueuse/head
// 在 router 配置里
const routes = [
{
path: '/dashboard',
component: () => import('@/views/Dashboard.vue'),
meta: { preload: true } // 标记需要预加载
},
{
path: '/detail',
component: () => import('@/views/Detail.vue')
}
]

// 在 App.vue 里
import { onMounted } from 'vue'
import { useRouter } from 'vue-router'

const router = useRouter()

onMounted(() => {
// 路由切换后预加载其他标记了的路由
router.afterEach((to) => {
routes.forEach(route => {
if (route.meta?.preload && route.path !== to.path) {
// 触发预加载,但不阻塞当前操作
router.getRoutes().find(r => r.path === route.path)?.match[0]?.components?.default
}
})
})
})


5. 关键组件直接渲染

如果首屏不需要等待整个路由加载完,可以先渲染一个骨架屏或者 Loading:

// App.vue



快速定位问题的方法

在你的入口文件 main.ts 开头加一行:

// 临时加的调试代码
window.addEventListener('DOMContentLoaded', () => {
console.timeEnd('首屏渲染')
})


然后看 Network 面板里,哪个请求耗时最长。如果是一个大 JS 文件,就检查打包配置;如果是很多小请求,就考虑合并。

还有一个容易忽略的点

检查一下 index.html 里的资源引用顺序:








Vite 默认生成的 index.html 这个顺序应该是对的,但如果你手改过可能出问题。

你先按我说的这几个点排查一下,特别是第一条的 vendor 分离,做完之后重新 build 看下产物大小变化。如果还有问题,把你的 vite.config.ts 和项目依赖贴出来,我帮你具体分析。
点赞
2026-03-11 08:07
Mr-博文
Mr-博文 Lv1
你路由写法没问题,组件懒加载是对的,问题不在这里。
首屏慢通常是因为没做代码分割后的预加载,或者打包产物太大没处理好。

先看几个关键点:

第一个,Vite 默认分包但不会预加载,你可以在 vite.config.js 里加个 manualChunks 或者用 build.rollupOptions 显式分包,比如这样改一下就行:

export default defineConfig({
build: {
rollupOptions: {
output: {
manualChunks(id) {
if (id.includes('node_modules')) {
return 'vendor'
}
}
}
}
}
})


第二个,首屏关键资源建议用 prefetchpreload,比如你在 HTML 里加个 <link rel="prefetch" href="/assets/xxx.js">,或者在路由里用 meta: { prefetch: true } 配合插件自动加。

第三个,检查打包产物大小,用 vite-bundle-visualizer 插件跑一下,看看哪个依赖占了大头,比如 lodashecharts 这类大包,换成 lodash-es 或按需引入。

还有个常见坑:别在入口文件里直接 import 大库,比如 import _ from 'lodash',这种会整个打进去,改成 import debounce from 'lodash/debounce' 就行。

最后,如果用了 CDN,记得开启 HTTP2 + gzip + brotli,nginx 里 brotli 要装模块,不是光配个 gzip on 就完事。

你先按这些排查一遍,Lighthouse 能提 20 分以上。
点赞 4
2026-02-26 16:03