16 KiB
16 KiB
tags, create time
| tags | create time | ||||
|---|---|---|---|---|---|
|
2026-04-18 |
Vite 优化实践
性能优化总览
graph TD
A[Vite 性能优化] --> B[开发环境]
A --> C[生产构建]
A --> D[运行时优化]
B --> B1[依赖预构建]
B --> B2[智能缓存]
B --> B3[热更新优化]
C --> C1[代码分割]
C --> C2[资源优化]
C --> C3[构建配置]
D --> D1[按需加载]
D --> D2[缓存策略]
D --> D3[监控分析]
开发环境优化
依赖预构建优化
智能依赖识别
import { defineConfig } from 'vite'
export default defineConfig({
optimizeDeps: {
// 手动指定需要预构建的依赖
include: [
'react',
'react-dom',
'antd',
'lodash'
],
// 排除不需要预构建的模块
exclude: [
// 已是 ESM 格式的包
'vue',
// 开发时频繁修改的包
'./src/common/*'
],
// 强制重新构建
force: process.env.NODE_ENV === 'development'
}
})
预构建性能调优
export default defineConfig({
optimizeDeps: {
// esbuild 配置
esbuildOptions: {
target: 'es2020',
// 保持类名和函数名,便于调试
keepNames: true,
// 启用打包内联
bundle: true,
// 外部化某些依赖
external: ['some-large-library']
}
}
})
服务器配置优化
高效的代理配置
export default defineConfig({
server: {
proxy: {
// API 代理
'/api': {
target: 'http://localhost:8080',
changeOrigin: true,
// 路径重写
rewrite: (path) => path.replace(/^\/api/, ''),
// 超时设置
timeout: 30000,
// 代理错误处理
configure: (proxy, _options) => {
proxy.on('error', (err, _req, res) => {
res.end(JSON.stringify({ error: err.message }))
})
}
},
// 静态资源代理
'/static': {
target: 'http://localhost:9000',
changeOrigin: true
}
}
}
})
开发服务器性能
export default defineConfig({
server: {
// 禁用自动浏览器打开
open: false,
// 严格的端口模式
strictPort: false,
// 仅监听 localhost
host: '127.0.0.1',
// HMR 配置
hmr: {
// 覆盖层配置
overlay: {
runtimeErrors: false, // 不显示运行时错误
errors: true, // 显示编译错误
warnings: false // 不显示警告
}
},
// 文件监听优化
watch: {
usePolling: false, // 禁用轮询(默认)
interval: 100, // 轮询间隔
ignored: [
'**/node_modules/**', // 忽略 node_modules
'**/.git/**', // 忽略 .git 目录
'**/dist/**', // 忽略构建输出
'coverage/**' // 忽略测试覆盖率
]
}
}
})
缓存策略
依赖缓存优化
export default defineConfig({
optimizeDeps: {
// 缓存目录
cacheDir: 'node_modules/.vite',
// 锁文件
lockfile: true,
// 禁用缓存调试
// force: true
}
})
// 清理依赖缓存
// npm run dev -- --force
模块缓存配置
export default defineConfig({
build: {
// 模块预加载
modulePreload: {
polyfill: false, // 禁用预加载 polyfill
resolveDependencies: false // 禁用依赖解析
}
}
})
生产构建优化
代码分割策略
智能代码分割
export default defineConfig({
build: {
rollupOptions: {
output: {
// 手动代码分割配置
manualChunks: (id) => {
// 节点模块分割
if (id.includes('node_modules')) {
if (id.includes('react')) {
return 'vendor-react'
}
if (id.includes('antd')) {
return 'vendor-ui'
}
if (id.includes('lodash') || id.includes('dayjs')) {
return 'vendor-utils'
}
return 'vendor-other'
}
// 应用代码分割
if (id.includes('pages/')) {
const pageName = id.split('/pages/')[1].split('/')[0]
return `pages/${pageName}`
}
// 组件分割
if (id.includes('components/')) {
const componentName = id.split('/components/')[1].split('/')[0]
return `components/${componentName}`
}
}
}
}
}
})
按路由懒加载
// React Router 懒加载示例
import { lazy, Suspense } from 'react'
// 路由组件懒加载
const HomePage = lazy(() => import(/* webpackChunkName: "home" */ '@/pages/Home'))
const AboutPage = lazy(() => import(/* webpackChunkName: "about" */ '@/pages/About'))
const DashboardPage = lazy(() => import(/* webpackChunkName: "dashboard" */ '@/pages/Dashboard'))
// 使用 Suspense 包裹
function App() {
return (
<Suspense fallback={<div>Loading...</div>}>
<Routes>
<Route path="/" element={<HomePage />} />
<Route path="/about" element={<AboutPage />} />
<Route path="/dashboard" element={<DashboardPage />} />
</Routes>
</Suspense>
)
}
资源优化
CSS 代码分割
export default defineConfig({
build: {
cssCodeSplit: true, // 启用 CSS 代码分割
cssTarget: 'chrome80', // CSS 目标浏览器
cssMinify: 'lightningcss' // CSS 压缩器
},
css: {
devSourcemap: false, // 开发环境不生成 Source Map
// PostCSS 配置
postcss: './postcss.config.js',
preprocessorOptions: {
scss: {
api: 'modern-compiler' // 使用现代编译器
}
}
}
})
静态资源优化
export default defineConfig({
build: {
// 资源处理
assetsInlineLimit: 4096, // 小于 4kb 的资源内联
rollupOptions: {
output: {
// 资源文件命名
assetFileNames: (assetInfo) => {
const info = assetInfo.name?.split('.') ?? []
const extType = info[info.length - 1]
if (/\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/i.test(assetInfo.name ?? '')) {
return `media/[name]-[hash].[ext]`
}
if (/\.(png|jpe?g|gif|svg|webp|avif)(\?.*)?$/i.test(assetInfo.name ?? '')) {
return `images/[name]-[hash].[ext]`
}
if (/\.(woff2?|eot|ttf|otf)(\?.*)?$/i.test(assetInfo.name ?? '')) {
return `fonts/[name]-[hash].[ext]`
}
return `assets/[name]-[hash].[ext]`
}
}
}
}
})
压缩与优化
代码压缩配置
export default defineConfig({
build: {
minify: 'terser', // 压缩器选择: terser | esbuild
terserOptions: {
compress: {
// 移除 console
drop_console: true,
drop_debugger: true,
// 纯函数优化
pure_funcs: [
'console.log',
'console.info',
'console.debug'
],
// 代码优化
ecma: 2020,
arguments: true,
dead_code: true,
side_effects: true
},
mangle: {
// 变量名混淆
toplevel: true,
properties: {
regex: /^_/ // 混淆以下划线开头的属性
},
keep_classnames: false,
keep_fnames: false
},
format: {
// 保留版权注释
comments: false,
// 不移除代码
preserveAnnotations: false
}
},
// 不显示压缩后大小
reportCompressedSize: false
}
})
构建产物分析
import { visualizer } from 'rollup-plugin-visualizer'
import { defineConfig } from 'vite'
export default defineConfig({
plugins: [
visualizer({
filename: './dist/stats.html',
open: true,
gzipSize: true,
brotliSize: true,
template: 'treemap' // treemap | sunburst | network
})
]
})
// 构建后分析
// npm run build
运行时优化
按需导入
UI 组件按需导入
// Ant Design 按需导入
import { ConfigProvider } from 'antd'
import Button from 'antd/es/button'
import Input from 'antd/es/input'
// 或者使用自动导入插件
import Components from 'unplugin-vue-components/vite'
import { AntDesignVueResolver } from 'unplugin-vue-components/resolvers'
export default defineConfig({
plugins: [
react(),
Components({
resolvers: [
AntDesignVueResolver({
importStyle: 'less' // 按需导入样式
})
]
})
]
})
工具库按需导入
// 原始导入方式(导入整个库)
// import _ from 'lodash'
// const result = _.map(arr, (item) => item.value)
// 按需导入方式
import { map } from 'lodash-es'
const result = map(arr, (item) => item.value)
// 或者使用 lodash-unified-plugin
缓存策略
HTTP 缓存配置
// Nginx 配置示例
location / {
try_files $uri $uri/ /index.html;
# 关键资源缓存策略
location ~* \.(js|css)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
# 图片资源缓存
location ~* \.(jpg|jpeg|png|gif|webp|svg)$ {
expires 6M;
add_header Cache-Control "public, max-age=15552000";
}
# HTML 不缓存
location ~* \.html$ {
add_header Cache-Control "no-cache, no-store, must-revalidate";
}
}
浏览器缓存优化
// 预加载关键资源
// 在 index.html 中添加
<link rel="modulepreload" href="/assets/index-[hash].js">
<link rel="prefetch" href="/assets/lazy-chunk-[hash].js">
// React 应用中的预加载
const preloadComponent = (path: string) => {
const link = document.createElement('link')
link.rel = 'prefetch'
link.href = path
document.head.appendChild(link)
}
// 根据路由预加载
const routes = [
{ path: '/dashboard', preload: () => preloadComponent('/assets/dashboard-[hash].js') }
]
性能监控
运行时性能监控
// 性能监控工具
class PerformanceMonitor {
private metrics: Map<string, number> = new Map()
measure(name: string, fn: () => void) {
const start = performance.now()
fn()
const duration = performance.now() - start
this.metrics.set(name, duration)
// 开发环境打印性能指标
if (import.meta.env.DEV) {
console.log(`[Performance] ${name}: ${duration.toFixed(2)}ms`)
}
}
getMetrics() {
return Object.fromEntries(this.metrics)
}
}
// 使用示例
const monitor = new PerformanceMonitor()
monitor.measure('app-init', () => {
initializeApp()
})
构建 time analysis
import { defineConfig } from 'vite'
export default defineConfig({
build: {
// 构建时间限制
chunkSizeWarningLimit: 1000,
// 构建并行化
parallel: true,
// 构建统计信息
reportCompressedSize: false
}
})
// 使用 time 命令测量构建时间
// time npm run build
实际案例分析
大型 React 项目优化
问题场景
- 🔧 项目包含 200+ 组件
- 📦 构建后的 main.js 超过 2MB
- 🚀 首屏加载时间 5-8 秒
优化方案
// vite.config.ts
export default defineConfig({
build: {
rollupOptions: {
output: {
manualChunks: {
// 核心框架
'react-core': ['react', 'react-dom', 'react-router-dom'],
// UI 组件库
'antd-core': ['antd'],
'antd-icons': ['@ant-design/icons'],
// 状态管理
'state-management': ['zustand', 'immer'],
// 工具库
'utils': ['lodash-es', 'dayjs', 'axios'],
// 业务模块
'business-user': [/src\/modules\/user/],
'business-order': [/src\/modules\/order/],
'business-product': [/src\/modules\/product/]
}
}
}
}
})
优化结果
- ✅ main.js 减少到 800KB
- ✅ 首屏加载时间降低到 2-3 秒
- ✅ 构建时间从 3 分钟减少到 1 分钟
TypeScript 项目编译优化
编译速度优化
// tsconfig.json
{
"compilerOptions": {
"incremental": true, // 增量编译
"tsBuildInfoFile": ".tsbuildinfo",
"skipLibCheck": true, // 跳过类型声明文件检查
"skipDefaultLibCheck": true
},
"references": [ // 项目引用
{ "path": "./packages/component" }
]
}
类型检查性能
// 按需类型检查
// .githooks/pre-commit
#!/bin/bash
git diff --cached --name-only | grep '\.tsx?$' | xargs npx tsc --noEmit
// 或者在开发环境只检查当前文件
// IDE 配置 TypeScript Server 模式
组件库开发优化
库模式配置
// vite.config.ts (组件库)
export default defineConfig({
build: {
lib: {
entry: path.resolve(__dirname, 'src/index.ts'),
name: 'MyComponentLibrary',
fileName: (format) => `my-component-library.${format}.js`,
formats: ['es', 'umd', 'cjs']
},
rollupOptions: {
// 外部化 react
external: ['react', 'react-dom'],
output: {
globals: {
react: 'React',
'react-dom': 'ReactDOM'
}
}
},
// 压缩
minify: 'terser',
sourcemap: true
}
})
监控与调试
开发环境监控
import { defineConfig } from 'vite'
export default defineConfig({
plugins: [
// 性能监控插件
{
name: 'performance-monitor',
transform(code, id) {
// 监控大文件
if (code.length > 100000) {
console.warn(`[Large File] ${id}: ${code.length} bytes`)
}
return null
}
}
]
})
生产环境调试
// Source Map 配置
export default defineConfig({
build: {
sourcemap: true, // 生产环境生成 Source Map
sourceMapDebug: false
}
})
// 部署环境配置错误的 Source Map
// 设置为 true 便于问题追踪,false 提高安全性
最佳实践总结
✅ 推荐实践
-
依赖管理
- 使用
include明确指定预构建依赖 - 定期清理依赖缓存
- 优先选择 ESM 格式的包
- 使用
-
代码组织
- 按路由和功能模块进行代码分割
- 使用动态导入实现懒加载
- 合理组织组件和工具函数
-
性能优化
- 启用 Tree-shaking 移除死代码
- 使用现代压缩工具
- 优化资源加载策略
-
监控分析
- 定期分析构建产物
- 监控应用运行性能
- 持续优化配置
❌ 避免问题
-
过度配置
- 不要过度使用配置插件
- 避免不必要的依赖
- 保持配置简洁
-
忽略缓存
- 不要忽略依赖缓存的清理
- 注意缓存失效策略
- 合理配置缓存时间
-
盲目优化
- 不要在没有性能问题时过度优化
- 优先解决真正的性能瓶颈
- 基于数据驱动的优化决策
工具推荐
构建分析工具
- vite-bundle-visualizer - 构建产物可视化
- rollup-plugin-visualizer - 深度构建分析
- source-map-explorer - Source Map 分析
性能测试工具
- Lighthouse - 页面性能评估
- WebPageTest - 多地性能测试
- Chrome DevTools Performance - 运行时性能分析
优化是一个持续的过程,需要根据项目的具体情况和性能数据来调整策略。通过合理的配置和分析工具,可以让 Vite 项目在各种场景下表现优异。
相关文档:
- DEV/VITE/配置详解.md - 详细配置说明
- DEV/VITE/架构原理.md - 理解优化原理
- DEV/VITE/README.md - 快速入门指南