Vite构建优化背景
Vite基于原生ESM和esbuild实现极速开发服务器启动,但生产构建使用Rollup打包,大型项目中构建耗时和产物体积仍是痛点。一个包含200个页面的中后台项目,冷构建耗时可能超过60秒,产物chunk过大导致首屏加载缓慢。通过手动配置Rollup参数、代码分割策略和Tree Shaking优化,构建时间可压缩一半以上,首屏加载体积减少40%。
依赖预构建与外部化
Vite的依赖预构建(optimizeDeps)将CommonJS/UMD依赖转换为ESM格式并缓存。大型依赖预构建耗时较长,可通过手动声明include列表锁定预构建范围,避免开发服务器冷启动时动态发现依赖导致的延迟。
// vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
export default defineConfig({
optimizeDeps: {
include: [
'vue',
'vue-router',
'pinia',
'element-plus',
'echarts',
'lodash-es',
'dayjs',
'axios'
],
exclude: ['@vueuse/core'] // 按需引入,不预构建
}
})
某些大型库(如xlsx、pdf-lib)仅在特定功能中使用,可完全外部化,通过CDN加载或动态import,避免打入主bundle:
export default defineConfig({
build: {
rollupOptions: {
external: ['xlsx'],
output: {
globals: { xlsx: 'XLSX' }
}
}
}
})
手动分块策略优化chunk体积
Vite默认按动态import自动分割chunk,但分割粒度不够精细,容易产生超大vendor chunk。通过manualChunks手动控制分块逻辑,将第三方依赖按功能分组:
export default defineConfig({
build: {
rollupOptions: {
output: {
manualChunks(id) {
if (id.includes('node_modules')) {
if (id.includes('vue') || id.includes('pinia')) {
return 'vue-vendor'
}
if (id.includes('element-plus')) {
return 'element-vendor'
}
if (id.includes('echarts')) {
return 'echarts-vendor'
}
if (id.includes('@vueuse') || id.includes('lodash-es')) {
return 'utils-vendor'
}
return 'vendor'
}
}
}
}
}
})
这套分块策略将vue核心、UI库、图表库、工具库分离为独立chunk。路由级别的业务代码按需加载,只有访问对应页面时才请求相应chunk。通过Webpack Bundle Analyzer或rollup-plugin-visualizer可视化分析chunk构成:
import { visualizer } from 'rollup-plugin-visualizer'
export default defineConfig({
plugins: [
vue(),
visualizer({
filename: 'dist/stats.html',
gzipSize: true,
brotliSize: true
})
]
})
第三方库按需引入减少Tree Shaking残留
Element Plus等组件库支持自动按需导入,但一些工具库默认全量引入导致产物膨胀。lodash是典型反面案例——全量引入约70KB,按需引入可降至10KB以内:
// 差: 全量引入
import _ from 'lodash'
_.debounce(fn, 300)
// 好: 按需引入
import debounce from 'lodash-es/debounce'
debounce(fn, 300)
// 或使用babel-plugin自动转换
// .babelrc
{
"plugins": [
["lodash", { "id": ["lodash-es"] }]
]
}
ECharts按需引入效果更为显著,全量引入约1MB,只引入折线图和柱状图可压缩至200KB:
import * as echarts from 'echarts/core'
import { LineChart, BarChart } from 'echarts/charts'
import { GridComponent, TooltipComponent, LegendComponent } from 'echarts/components'
import { CanvasRenderer } from 'echarts/renderers'
echarts.use([
LineChart, BarChart,
GridComponent, TooltipComponent, LegendComponent,
CanvasRenderer
])
// 创建图表实例
const chart = echarts.init(document.getElementById('chart'))
chart.setOption(option)
CSS代码分割与PurgeCSS
Element Plus等UI库的CSS文件体积较大,全量引入约300KB。Vite默认对CSS进行代码分割,但未使用组件的样式仍会打包。配合unplugin-auto-import和unplugin-vue-components实现组件和样式的自动按需引入:
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
export default defineConfig({
plugins: [
vue(),
AutoImport({
resolvers: [ElementPlusResolver()]
}),
Components({
resolvers: [ElementPlusResolver()]
})
]
})
配置后,模板中直接使用el-table、el-button等组件会自动引入对应样式,未使用的组件样式不会打包。
Gzip与Brotli预压缩
Vite构建默认生成未压缩文件,由Nginx在运行时Gzip压缩。预压缩可减少服务端CPU占用并加速传输。Brotli压缩率比Gzip高15%-20%,现代浏览器均支持。通过vite-plugin-compression生成预压缩文件:
import viteCompression from 'vite-plugin-compression'
export default defineConfig({
plugins: [
vue(),
viteCompression({
algorithm: 'gzip',
ext: '.gz',
threshold: 10240 // 仅压缩大于10KB的文件
}),
viteCompression({
algorithm: 'brotliCompress',
ext: '.br',
threshold: 10240
})
]
})
Nginx配置优先使用Brotli预压缩文件:
# nginx.conf
brotli_static on;
gzip_static on;
gzip on;
gzip_types text/plain text/css application/javascript application/json;
构建缓存与增量编译
Vite 5+版本支持Rollup的持久化缓存,二次构建速度显著提升。配合cacheDir和fileAgingQueue可加速重建。对于Monorepo项目,通过tsconfig的paths映射和Vite的resolve.alias避免重复编译:
export default defineConfig({
resolve: {
alias: {
'@': '/src',
'@components': '/src/components',
'@utils': '/src/utils'
},
dedupe: ['vue', 'vue-router', 'pinia'] // 确保单例
},
build: {
cacheOptions: {
cacheDir: 'node_modules/.vite-cache'
}
}
})
CI/CD环境中利用pnpm store缓存和Vite构建缓存,二次构建时间可从60秒压缩至20秒以内。确保CI runner的缓存目录正确挂载:
# GitHub Actions缓存配置
- name: Cache Vite Build
uses: actions/cache@v3
with:
path: |
node_modules/.vite-cache
node_modules/.vite
key: ${{ runner.os }}-vite-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-vite-
资源内联与Base64阈值
小体积图片和SVG内联为Base64可减少HTTP请求数,但Base64编码会使体积增加33%。assetsInlineLimit控制内联阈值,默认4096字节。根据HTTP/2多路复用特性,建议将阈值降低至2048字节:
export default defineConfig({
build: {
assetsInlineLimit: 2048,
rollupOptions: {
output: {
assetFileNames: (assetInfo) => {
if (assetInfo.name.endsWith('.png')) {
return 'assets/img/[name].[hash][extname]'
}
return 'assets/[name].[hash][extname]'
},
chunkFileNames: 'assets/js/[name].[hash].js',
entryFileNames: 'assets/js/[name].[hash].js'
}
}
}
})
大图片使用vite-plugin-imagemin进行无损压缩,SVG使用vite-plugin-svgr转换为Vue组件按需引入。通过这套组合策略,200页面级别的中后台项目首屏JS体积可从2.5MB降至1.2MB,LCP(最大内容渲染时间)从4.2秒优化至1.8秒。
原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/vite-sheng-chan-gou-jian-you-hua-shi-zhan-shou-dong-fen/