Vite凭借ESM原生模块加载和esbuild预构建,在开发体验上远超Webpack。但生产构建阶段,Vite底层使用Rollup,构建产物体积和加载性能仍需手动调优。本文涵盖Vite预构建机制、代码分割策略、打包体积分析和构建产物优化的完整方案。
Vite依赖预构建机制与优化配置
Vite开发模式下通过esbuild将CommonJS/UMD依赖预构建为ESM格式,缓存到node_modules/.vite目录。预构建解决两个问题:将CJS转为ESM供浏览器原生import加载;将多入口小文件依赖(如lodash-es数百个模块)合并为单文件减少HTTP请求。生产构建使用Rollup,不依赖预构建缓存,但构建配置影响最终产物。
// vite.config.ts 预构建优化
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
export default defineConfig({
plugins: [react()],
optimizeDeps: {
include: ['react', 'react-dom', 'react-router-dom', 'axios', 'dayjs', 'zustand'],
exclude: ['@cdn-hosted/large-lib'],
esbuildOptions: {
target: 'es2020',
legalComments: 'none'
}
},
build: {
target: 'es2020',
sourcemap: 'hidden',
minify: 'esbuild',
chunkSizeWarningLimit: 1000,
rollupOptions: {
output: {
chunkFileNames: 'assets/js/[name]-[hash].js',
entryFileNames: 'assets/js/[name]-[hash].js',
assetFileNames: 'assets/[ext]/[name]-[hash].[ext]'
}
}
}
})
开发时若发现首次加载页面后浏览器刷新(依赖未预构建导致),将对应包加入include列表即可。optimizeDeps.include在Vite 5+中已能自动检测大部分依赖,但某些动态import的依赖仍需手动声明。
代码分割策略与动态导入
Vite生产构建默认对动态import()进行代码分割,按路由或功能模块拆分chunk,减少首屏加载体积。
// 路由级懒加载(React Router)
import { lazy, Suspense } from 'react'
import { BrowserRouter, Routes, Route } from 'react-router-dom'
const Dashboard = lazy(() => import('./pages/Dashboard'))
const UserList = lazy(() => import('./pages/UserList'))
const Settings = lazy(() => import('./pages/Settings'))
function App() {
return (
<BrowserRouter>
<Suspense fallback={<div>loading...</div>}>
<Routes>
<Route path="/dashboard" element={<Dashboard />} />
<Route path="/users" element={<UserList />} />
<Route path="/settings" element={<Settings />} />
</Routes>
</Suspense>
</BrowserRouter>
)
}
// 组件级懒加载(大体积组件)
const RichEditor = lazy(() => import('./components/RichEditor'))
// 条件加载(仅在需要时加载)
async function exportToExcel() {
const { exportExcel } = await import('./utils/excel-export')
exportExcel(data)
}
// vite.config.ts 手动分包策略
export default defineConfig({
build: {
rollupOptions: {
output: {
manualChunks: {
'react-vendor': ['react', 'react-dom', 'react-router-dom'],
'ui-vendor': ['antd', '@ant-design/icons'],
'utils-vendor': ['axios', 'dayjs', 'lodash-es'],
'chart-vendor': ['echarts', 'echarts-for-react']
}
}
}
}
})
manualChunks按依赖来源分组,将第三方库从业务代码中分离。好处是库代码变动少,缓存命中率高。注意不要在manualChunks中包含动态导入的模块,否则Rollup可能报循环依赖警告。对于antd等支持Tree Shaking的库,确保按需导入而非全量引入。
构建产物分析与打包体积优化
使用rollup-plugin-visualizer生成可视化分析报告,定位体积瓶颈。
import { visualizer } from 'rollup-plugin-visualizer'
export default defineConfig({
plugins: [
react(),
visualizer({
open: true,
filename: 'dist/stats.html',
gzipSize: true,
brotliSize: true
})
]
})
常见体积问题排查:dayjs替代moment.js可节省约60KB gzip;lodash-es配合Tree Shaking比lodash全量引入节省约20KB;antd按需导入比全量引入节省约100KB+;图片资源使用vite-plugin-imagemin压缩或转为WebP格式。
// CSS优化:PurgeCSS去除未使用样式
import purgeCSS from 'vite-plugin-purgecss'
export default defineConfig({
plugins: [
purgeCSS({
content: ['index.html', 'src/**/*.{ts,tsx}'],
safelist: [/ant-/]
})
]
})
Gzip与Brotli预压缩降低传输体积
Vite构建产物默认不带预压缩文件,通过插件生成.gz和.br文件,配合Nginx静态文件服务直接返回预压缩内容。
import viteCompression from 'vite-plugin-compression'
export default defineConfig({
plugins: [
viteCompression({
algorithm: 'gzip',
ext: '.gz',
threshold: 10240
}),
viteCompression({
algorithm: 'brotliCompress',
ext: '.br',
threshold: 10240
})
]
})
# Nginx配置:优先返回Brotli,回退Gzip
server {
gzip_static on;
brotli_static on;
location /assets/ {
try_files $uri $uri.br $uri.gz $uri;
add_header Cache-Control "public, max-age=31536000, immutable";
}
}
hash文件名配合immutable缓存策略,确保资源更新与缓存命中率兼顾。Vite的contenthash使文件内容变化时hash自动更新,浏览器缓存自动失效。对于不变的vendor chunk,设置一年强缓存后,用户后续访问仅需请求变化的业务chunk,大幅提升页面加载速度。
原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/vite-gou-jian-you-hua-shi-zhan-yi-lai-yu-gou-jian-yu-dai-ma/