项目技术栈:react18.2/redux+ant5.28+zustand+vite6.2.0
基础配置
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import vitePluginImp from 'vite-plugin-imp';
import svgr from 'vite-plugin-svgr';
import path from 'path';
export default defineConfig({
define: {__BUILD_TIME__: JSON.stringify(Date.now()),},
plugins: [
react(),
svgr({
svgrOptions: {icon: true}
}),
vitePluginImp({libList: [{libName: 'antd'}]})],
base: './',
server: {
host: '0.0.0.0', // 允许局域网访问
port: 1234, // 指定端口
open: true, // 自动打开浏览器
proxy: {.......},
css: {
preprocessorOptions: { less: { javascriptEnabled: true } },
postcss: './postcss.config.js'
},
resolve: {
alias: {'@': path.resolve(__dirname, './src')}
}
})
分包后配置
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import svgr from 'vite-plugin-svgr';
import path from 'path';
import { visualizer } from 'rollup-plugin-visualizer';
export default defineConfig({
define: {
__BUILD_TIME__: JSON.stringify(Date.now())
},
plugins: [
react(),
svgr({
svgrOptions: {
icon: true
}
}),
visualizer({
open: true,
gzipSize: true,
brotliSize: true,
filename: 'dist/stats.html'
})
],
base: './',
build: {
// 设置输出目录
outDir: 'dist',
// 静态资源目录
assetsDir: 'assets',
// chunk 大小警告的限制(单位:KB)
chunkSizeWarningLimit: 1000,
rollupOptions: {
output: {
// 分包策略 - 保守但稳定的方案
manualChunks: id => {
if (id.includes('node_modules')) {
// React + Ant Design 一起打包(UI 框架核心)
// 【路由】React Router 可以单独打包(依赖关系清晰)
if (
id.includes('/react-router') ||
id.includes('/@remix-run/') ||
id.includes('/react-activation/')
) {
return 'router-vendor';
}
// 【状态管理】Redux 相关
if (
id.includes('/redux') ||
id.includes('/@reduxjs/') ||
id.includes('/react-redux') ||
id.includes('/zustand')
) {
return 'state-vendor';
}
// dayjs 时间处理库
if (id.includes('/dayjs')) {
return 'dayjs-vendor';
}
// i18n 国际化相关
if (id.includes('/i18next') || id.includes('/react-i18next')) {
return 'i18n-vendor';
}
// 图表库独立打包
if (id.includes('/echarts') || id.includes('/zrender')) {
return 'echarts-vendor';
}
// lodash 工具库
if (id.includes('/lodash')) {
return 'lodash-vendor';
}
// xlsx 表格处理
if (id.includes('/xlsx')) {
return 'xlsx-vendor';
}
// 其他第三方库
return 'common-vendor';
}
},
// 用于从入口点创建的块的打包输出格式
entryFileNames: 'js/[name]-[hash].js',
// 用于命名代码拆分时创建的共享块的输出命名
chunkFileNames: 'js/[name]-[hash].js',
// 用于输出静态资源的命名
assetFileNames: assetInfo => {
// 图片文件
if (/\.(png|jpe?g|gif|svg|webp|ico)$/.test(assetInfo.name)) {
return 'images/[name]-[hash][extname]';
}
// 字体文件
if (/\.(woff2?|eot|ttf|otf)$/.test(assetInfo.name)) {
return 'fonts/[name]-[hash][extname]';
}
// CSS 文件
if (/\.css$/.test(assetInfo.name)) {
return 'css/[name]-[hash][extname]';
}
// 其他资源
return 'assets/[name]-[hash][extname]';
}
}
},
// 压缩选项 - 使用 esbuild(Vite 内置,速度快)
minify: 'esbuild',
// 启用/禁用 CSS 代码拆分
cssCodeSplit: true,
// 构建后是否生成 source map 文件(调试时开启,生产环境关闭)
sourcemap: process.env.NODE_ENV !== 'production'
},
server: {
host: '0.0.0.0', // 允许局域网访问
port:1234, // 指定端口
open: true, // 自动打开浏览器
proxy: {
...
}
},
css: {
preprocessorOptions: {
less: {
javascriptEnabled: true
}
},
postcss: './postcss.config.js'
},
resolve: {
alias: {
'@': path.resolve(__dirname, './src')
}
}
});
打包分析结果

image.png
分包过程出现的问题
分包之后,发现有一个相对较大的文件

image.png
在上面的分包策略之后我又对这个大文件拆分,把react,ant拆分为单独的包(如下代码,打包过程不会报错,但是系统运行会报错)
if (
id.includes('/react/') ||
id.includes('/react-dom/') ||
id.includes('/react-is/') ||
id.includes('/scheduler/')
) {
return 'react-vendor';
}
// Ant Design 及其 rc-* 组件单独打包(更新频率相对较高)
if (
id.includes('/antd/') ||
id.includes('/@ant-design/') ||
id.includes('/rc-')
) {
return 'antd-vendor';
}

screenshot_2025-11-27_11-07-13.png
2:报错原因
应该是模块依赖顺序问题,这是因为 rc-* 组件(Ant Design 的底层组件)依赖 React,当它们被拆分到不同的 chunk 后,会出现模块初始化顺序问题和循环依赖。(本人猜测如果有更好的解决方案请留言)
分包和不分包的优劣对比
基础指标对比

image.png
文件大小对比

image.png
緩存效果 (优势)

image.png
真实场景分析

image.png