
在最近的一个中后台管理系统的开发中,我们采用了 Vue 3 + TypeScript + Vite 这一现代技术栈。开发阶段,得益于Vite极速的热更新,体验非常丝滑。然而,在项目构建上线后,我们却收到了用户的反馈:“首页打开速度很慢,尤其是第一次访问的时候,白屏时间过长。”
通过Chrome DevTools的Lighthouse工具和Network面板进行分析,我们发现了几个核心问题:
element-plus, lodash-es) 未做有效Tree Shaking和CDN优化。这显然无法令人接受。于是,我们开启了一场针对性的性能优化之旅。
我们的优化围绕一个核心原则:尽可能减少首屏需要加载和解析执行的资源量。
场景:我们的项目有几十个路由页面,如果全部打包到一个文件中,首加载体积必然巨大。
操作:Vue Router天然支持动态导入,Webpack和Vite都会自动将其识别为代码分割点。
关键代码(优化前 vs 优化后):
// 优化前:静态导入,所有页面打包到一个chunk中
import Home from '@/views/Home.vue'
import UserList from '@/views/UserList.vue'
const routes = [
{ path: '/', component: Home },
{ path: '/users', component: UserList }
]// 优化后:使用动态导入,生成单独的chunk
const routes = [
{
path: '/',
component: () => import('@/views/Home.vue') // 会产生一个单独的chunk
},
{
path: '/users',
component: () => import('@/views/UserList.vue') // 会产生另一个单独的chunk
}
]效果:现在,只有用户访问特定路由时,才会加载对应页面的JavaScript代码。首屏只需要加载Home.vue相关的代码,体积瞬间减小。
场景:即使做了路由懒加载,首屏主包(index.[hash].js)依然很大,因为它包含了所有初始依赖的第三方库(如vue, element-plus, axios, lodash-es)。
思路:利用Vite(底层基于Rollup)的manualChunks配置,将几乎不会改变的第三方库单独打包,利用浏览器缓存机制(强缓存),用户第二次访问时就不需要再次下载这些文件了。
关键配置(vite.config.ts):
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue()],
build: {
rollupOptions: {
output: {
// 手动拆包
manualChunks: (id) => {
if (id.includes('node_modules')) {
// 将第三方库拆成单独的chunk
// 1. 将vue相关的库打包到一起
if (id.includes('vue') || id.includes('@vue')) {
return 'vue-vendor'
}
// 2. 将element-plus打包到一个 chunk
if (id.includes('element-plus')) {
return 'element-plus-vendor'
}
// 3. 其他第三方库打包到一起
return 'vendor'
}
}
}
}
}
})同时,确保Tree Shaking生效:
对于element-plus和lodash-es,我们必须按需导入,而不是全量导入。
// 错误示例(全量导入): main.ts
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
app.use(ElementPlus)
// 正确示例(按需导入): 1. 安装unplugin-vue-components 2. 修改vite.config.ts
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
export default defineConfig({
plugins: [
vue(),
Components({
resolvers: [ElementPlusResolver()], // 自动按需导入Element Plus组件
}),
],
})// lodash-es 也应使用具名导入,而不是导入整个库
// 错误示例
import _ from 'lodash-es'
_.debounce()
// 正确示例
import { debounce } from 'lodash-es'
debounce()效果:vue, element-plus等依赖被提取到独立的、可长期缓存的chunk中。同时,Tree Shaking移除了未使用的组件和函数,进一步减小了体积。
场景:网络传输体积依然有优化空间。
操作:Vite开箱即用提供了资源的压缩(通过terser进行代码压缩混淆)。此外,我们可以在Nginx服务器上开启Gzip压缩,进一步减少传输体积。
Nginx配置示例(nginx.conf):
http {
gzip on; # 开启gzip
gzip_min_length 1k; # 大于1KB的文件才压缩
gzip_comp_level 6; # 压缩级别(1-9),通常6是性价比最高的
gzip_types text/plain text/css text/xml application/json application/javascript application/xml+rss; # 压缩类型
gzip_vary on;
}效果:我们的JavaScript文件在网络传输中的体积减少了60%-70%。
优化前后,我们使用 npm run build 打包并通过Lighthouse进行分析,对比如下:
指标 | 优化前 | 优化后 | 提升 |
|---|---|---|---|
首屏加载时间 | ~3500ms | ~800ms | 77% |
最大内容绘制 (LCP) | 3.2s | 1.1s | 65% |
打包总体积 | ~3.2MB | ~1.5MB | 53% |
首屏主包体积 | ~1.2MB | ~350KB | 71% |
这次优化实践让我深刻体会到,性能优化是一个系统工程,需要从代码组织、构建工具配置和部署环境三个层面共同发力。
manualChunks、Tree Shaking等功能是优化打包结果的利器。下一步,我们还可以考虑:
vite-plugin-pwa为应用添加PWA特性,利用Service Worker实现缓存和离线访问。rollup-plugin-visualizer),进一步找出可优化的依赖项。希望这篇实战记录能对你的项目有所帮助!如果你有更好的优化方案,欢迎在评论区交流。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。