vite打包的分包策略优化

项目技术栈: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
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容