Vite 构建优化与部署
生产环境的构建产物直接影响用户体验。本文涵盖性能分析、产物优化、部署策略和常见问题排查。
1. 构建产物分析
1.1 可视化分析
bash
# 安装 rollup-plugin-visualizer
pnpm add -D rollup-plugin-visualizertypescript
// vite.config.ts
import { visualizer } from 'rollup-plugin-visualizer'
export default defineConfig({
plugins: [
visualizer({
filename: 'stats.html',
open: true,
gzipSize: true,
brotliSize: true
})
]
})bash
pnpm build # 生成 stats.html 可视化报告1.2 产物体积检查
bash
# 查看各 chunk 大小
vite build --mode production
# 输出示例:
# dist/assets/index-a1b2c3.js 142.56 kB │ gzip: 45.21 kB
# dist/assets/vendor-d4e5f6.js 289.34 kB │ gzip: 92.18 kB
# dist/assets/echarts-g7h8i9.js 812.45 kB │ gzip: 261.03 kBchunk 体积红线
- 单个 chunk > 500kB(gzip 前)会触发警告
- 首屏 JS 总量建议 < 200kB(gzip 后)
- 图片资源优先使用 WebP/AVIF + CDN
2. 代码分割优化
2.1 路由级分割
typescript
// router/index.ts
const routes = [
{
path: '/',
component: () => import('@/views/Home.vue') // 独立 chunk
},
{
path: '/dashboard',
component: () => import('@/views/Dashboard.vue')
},
{
path: '/settings',
// webpackChunkName 注释在 Vite 中无效
// 使用 rollup 的 manualChunks 或输出命名
component: () => import('@/views/Settings.vue')
}
]2.2 manualChunks 策略
typescript
export default defineConfig({
build: {
rollupOptions: {
output: {
manualChunks: {
// 框架核心(极少变动 → 长期缓存)
'framework': ['vue', 'vue-router', 'pinia'],
// UI 组件库
'ui': ['ant-design-vue', '@ant-design/icons-vue'],
// 工具库
'utils': ['lodash-es', 'dayjs', 'axios'],
// 图表(体积大,按需加载)
'charts': ['echarts', 'zrender']
}
}
}
}
})2.3 分割原则
图表渲染中…
| 原则 | 说明 |
|---|---|
| 按变动频率分 | 不常变的依赖独立缓存,避免业务更新导致依赖缓存失效 |
| 按体积分 | 超大库(echarts/three.js)独立 chunk + 动态 import |
| 避免过度分割 | chunk 过多增加 HTTP 请求数,HTTP/2 下建议 5-15 个 |
| 入口 chunk 最小化 | 首屏只加载必要代码,其余懒加载 |
3. 资源优化
3.1 图片优化
bash
pnpm add -D vite-plugin-imagemintypescript
import viteImagemin from 'vite-plugin-imagemin'
export default defineConfig({
plugins: [
viteImagemin({
gifsicle: { optimizationLevel: 3 },
optipng: { optimizationLevel: 7 },
mozjpeg: { quality: 80 },
pngquant: { quality: [0.65, 0.8], speed: 4 },
svgo: {
plugins: [
{ name: 'removeViewBox', active: false },
{ name: 'removeEmptyAttrs', active: true }
]
}
})
]
})3.2 字体子集化
bash
pnpm add -D vite-plugin-fontstypescript
import { VitePluginFonts } from 'vite-plugin-fonts'
export default defineConfig({
plugins: [
VitePluginFonts({
custom: {
families: [{
name: 'MyFont',
local: 'MyFont',
src: './src/assets/fonts/*.woff2'
}],
display: 'swap',
preload: true
}
})
]
})3.3 压缩策略
typescript
export default defineConfig({
build: {
// esbuild 压缩(默认,速度最快)
minify: 'esbuild',
// 或 terser(压缩率略高,速度慢)
minify: 'terser',
terserOptions: {
compress: {
drop_console: true, // 移除 console
drop_debugger: true, // 移除 debugger
pure_funcs: ['console.log'] // 移除指定函数调用
},
format: {
comments: false // 移除注释
}
}
}
})4. 预渲染与 SSG
4.1 vite-ssg
bash
pnpm add vite-ssgtypescript
// src/main.ts
import { ViteSSG } from 'vite-ssg'
import App from './App.vue'
export const createApp = ViteSSG(App, { routes }, ({ app, router }) => {
// 安装插件
})4.2 vite-plugin-ssr(Vike)
适用于需要 SSR/SSG 的复杂应用:
bash
pnpm add -D vite-plugin-ssrcode
pages/
├── index/
│ ├── +Page.vue
│ ├── +data.ts ← 数据获取
│ └── +config.ts ← 页面配置
└── about/
└── +Page.vue5. 部署方案
5.1 静态部署(CDN / Nginx)
nginx
server {
listen 80;
server_name example.com;
root /var/www/my-app/dist;
index index.html;
# 带 hash 的资源 → 强缓存
location /assets/ {
expires 1y;
add_header Cache-Control "public, immutable";
}
# HTML → 协商缓存
location / {
try_files $uri $uri/ /index.html;
add_header Cache-Control "no-cache";
}
# Gzip / Brotli
gzip on;
gzip_types text/plain application/javascript text/css application/json;
gzip_min_length 1024;
}5.2 Vercel 部署
json
// vercel.json
{
"buildCommand": "pnpm build",
"outputDirectory": "dist",
"framework": "vite",
"rewrites": [
{ "source": "/(.*)", "destination": "/index.html" }
]
}5.3 Docker 部署
dockerfile
# 构建阶段
FROM node:20-alpine AS builder
WORKDIR /app
COPY package.json pnpm-lock.yaml ./
RUN corepack enable && pnpm install --frozen-lockfile
COPY . .
RUN pnpm build
# 运行阶段
FROM nginx:alpine
COPY --from=builder /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]6. 性能监控
6.1 构建耗时分析
bash
# 使用 --profile 生成 CPU profile
vite build --profile
# 用 Chrome DevTools → Performance → Load profile 分析6.2 运行时性能指标
typescript
// 在入口文件注入 Web Vitals 采集
import { onCLS, onFID, onLCP, onFCP, onTTFB } from 'web-vitals'
function reportMetric(metric) {
navigator.sendBeacon('/analytics', JSON.stringify({
name: metric.name,
value: metric.value,
rating: metric.rating
}))
}
onCLS(reportMetric)
onFID(reportMetric)
onLCP(reportMetric)
onFCP(reportMetric)
onTTFB(reportMetric)7. 常见问题排查
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 构建后白屏 | base 配置错误 | 检查部署路径与 base 是否匹配 |
| 依赖预构建缓存过期 | 锁定文件变更 | rm -rf node_modules/.vite && pnpm dev |
| 动态 import 路径报错 | 变量路径无法静态分析 | 使用 import.meta.glob 或固定前缀 |
| CSS 顺序不一致 | 异步 chunk 加载顺序不确定 | 使用 cssCodeSplit: false 或明确 import 顺序 |
| 构建产物过大 | 全量引入组件库 | 使用按需导入插件(unplugin-vue-components) |
| HMR 失效 | 循环依赖/副作用模块 | 检查模块是否有顶层副作用 |
import.meta.glob 替代动态路径
typescript
// 错误:变量路径无法被 Rollup 静态分析
const module = await import(`./views/${name}.vue`)
// 正确:使用 glob 预收集
const modules = import.meta.glob('./views/*.vue')
const module = await modules[`./views/${name}.vue`]()