路由懒加载 → manualChunks 分包 → 图片压缩,真实工程里的三件套
以下内容仅供参考酌情使用。说起 Vite 和性能优化,网上大多都是讲概念(什么是分包、什么是懒加载、什么是 Tree Shaking),但很多文章只讲理论,不讲工程实践。
很多人误以为 Vite 会把所有代码打成一个 JS。其实 Vite 的生产构建基于 Rollup,默认会做基础的 chunk splitting(代码拆分),例如 main.js / vendor.js。但默认拆分不会按照业务语义优化,可能出现:
首页代码 + lodash
报表代码 + lodash
编辑器代码 + lodash
最基础也是最重要的一步:按页面加载代码。如果不做懒加载,用户打开首页要下载整个项目所有页面代码;做了懒加载,打开首页只加载首页代码,进入报表页再加载报表代码——首屏体积显著下降。
// React 示例
import { lazy, Suspense } from 'react'
const Home = lazy(() => import('./pages/Home'))
const Report = lazy(() => import('./pages/Report'))
const Editor = lazy(() => import('./pages/Editor'))
function App() {
return (
<Suspense fallback={<div>加载中...</div>}>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/report" element={<Report />} />
<Route path="/editor" element={<Editor />} />
</Routes>
</Suspense>
)
}
懒加载解决的是业务代码拆分,但第三方依赖仍然可能混在业务 chunk 里:
page-home.js
└ react
└ lodash
page-report.js
└ echarts
└ lodash
如果业务代码更新:page-home hash 改变 → lodash 也被重新下载 → 浏览器缓存失效。
manualChunks 可以手动控制依赖如何分包,把长期不变的依赖拆出来(react-vendor.js / utils.js / echarts.js / editor.js),业务更新时只更新业务 chunk,vendor 继续使用缓存。
rollupOptions: {
output: {
manualChunks: {
'react-vendor': [
'react',
'react-dom',
'react-router-dom',
'zustand'
],
'arco-design': ['@arco-design/web-react'],
echarts: ['@ceai-front/echarts'],
wangeditor: [
'@wangeditor-next/editor',
'@wangeditor-next/editor-for-react'
],
utils: [
'lodash',
'lodash-es',
'dayjs',
'axios',
'classnames'
]
}
}
}
manualChunks(id) {
if (id.includes('node_modules')) {
if (id.includes('react')) {
return 'react-vendor'
}
if (id.includes('arco')) {
return 'arco'
}
if (id.includes('echarts')) {
return 'echarts'
}
}
}
node_modules/react/index.js),函数写法在复杂依赖场景中更稳定。在很多项目里,图片往往才是体积最大的资源:banner.jpg 2MB、icon.png 800KB、background 3MB……一旦页面加载多张图片,性能会明显下降。
pnpm add vite-plugin-imagemin -D
import viteImagemin from 'vite-plugin-imagemin'
plugins: [
viteImagemin({
gifsicle: { optimizationLevel: 3 },
optipng: { optimizationLevel: 7 },
mozjpeg: { quality: 80 },
pngquant: { quality: [0.7, 0.8] },
svgo: {
plugins: [{ name: 'removeViewBox' }]
}
})
]
| 格式 | 通常可减少 |
|---|---|
| PNG | 20% ~ 40% |
| JPG | 10% ~ 30% |
| SVG | 50%+ |
一个容易忽略的小优化:
assetsInlineLimit: 4096
意思是:小于 4KB 的资源会被转换为 Base64 并直接内联到 JS 中,减少 HTTP 请求数量。但不宜设置过大,否则 JS 体积会膨胀。
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import viteImagemin from 'vite-plugin-imagemin'
export default defineConfig({
plugins: [
react(),
viteImagemin({
gifsicle: { optimizationLevel: 3 },
optipng: { optimizationLevel: 7 },
mozjpeg: { quality: 80 },
pngquant: { quality: [0.7, 0.8] },
svgo: { plugins: [{ name: 'removeViewBox' }] }
})
],
build: {
target: 'esnext',
minify: 'esbuild',
cssCodeSplit: true,
sourcemap: false,
chunkSizeWarningLimit: 2000,
assetsInlineLimit: 4096,
rollupOptions: {
output: {
manualChunks: {
'react-vendor': [
'react',
'react-dom',
'react-router-dom',
'zustand'
],
'arco-design': ['@arco-design/web-react'],
echarts: ['@ceai-front/echarts'],
pdfjs: ['pdfjs-dist'],
utils: [
'lodash',
'lodash-es',
'dayjs',
'axios',
'classnames'
]
},
assetFileNames: 'assets/[name].[hash][extname]',
chunkFileNames: 'js/[name].[hash].js',
entryFileNames: 'js/[name].[hash].js'
}
}
}
})
| 优化手段 | 解决的问题 | 口诀 |
|---|---|---|
| 1️⃣ 路由懒加载 | 控制什么时候加载代码 → 首屏体积大幅下降 | 懒加载 = 控制加载时机 |
| 2️⃣ manualChunks 分包 | 把长期不变的依赖单独打包 → 浏览器缓存最大化 | 分包 = 控制缓存策略 |
| 3️⃣ 图片压缩 | 减少最大体积资源 → 页面加载速度明显提升 | 图片压缩 = 减少资源体积 |
| 术语 | 定义 |
|---|---|
| Code Splitting | 代码拆分,按需加载不同 chunk,避免首屏加载全部代码 |
| manualChunks | Rollup 手动分包配置,控制第三方依赖的 chunk 归属 |
| Tree Shaking | 构建时移除未使用代码(死代码消除) |
| assetsInlineLimit | 小于该字节数的静态资源转 Base64 内联,减少请求数 |
| vite-plugin-imagemin | Vite 图片压缩插件,自动优化 PNG/JPG/SVG 等格式 |
| chunkSizeWarningLimit | chunk 体积告警阈值,超过会输出构建警告 |