UmiJS 4 打包产物瘦身指南:从 2.6MB 的 umi.js 到按路由按需加载
2026/9/11 1:36:18 网站建设 项目流程

UmiJS 4 打包产物瘦身指南:从 2.6MB 的 umi.js 到按路由按需加载

【免费下载链接】umiA framework in react community ✨项目地址: https://gitcode.com/GitHub_Trending/um/umi

生产环境里 umi.js 动辄两三 MB,首屏要等主文件下载解析完才能渲染。Umi 4 的解法是分层分包:路由级拆包默认就开,再叠加 codeSplitting 策略把公共依赖切出去,最后用 ANALYZE 验证产物构成。按下面的顺序操作,主包通常能减掉一半以上体积,且旧访客的缓存命中率明显提高。

先搞清楚 Umi 4 默认帮你做了什么

很多人以为"umi.js 太大"是框架不会拆包,其实 Umi 4 默认就按路由拆分 chunk、按需加载——访问哪个页面才加载哪个页面的 JS,这点和 Umi 3 里手动开 dynamicImport 的效果一致。

真正的问题出在"公共部分":react、react-router、lodash 这类被多个页面引用的依赖,默认会反复打进各路由 chunk,或者整体塞进 umi.js 主文件。优化的核心不是"更激进的拆法",而是把这些公共依赖按缓存友好的粒度切出去。

第一步:开启 granularChunks 策略(两行配置)

.umirc.tsconfig/config.ts里加一段配置即可:

// .umirc.ts export default { // 其余配置省略 codeSplitting: { jsStrategy: 'granularChunks', }, };

为什么选它而不是另外两个选项?Umi 4 内置三种策略,差异如下:

策略拆法优点代价适用场景
bigVendors所有 node_modules 合成一个 vendors 包无重复、请求少单文件巨大,版本一变全员重下小项目、内网应用
depPerChunk按包名 + 版本各自成包缓存粒度细大型项目请求数多HTTP/2 下的中型项目
granularChunksframework 包 + 大依赖包 + 共享包三层切分体积与缓存效率平衡配置即默认推荐绝大多数项目的默认选择

granularChunks 内部逻辑可以直接看功能源码:react、react-dom、scheduler、react-router 等被强制归入 framework chunk;超过 160KB 的大依赖各自独立;被两个以上路由引用的模块合并为 shared-xxx。三层分工保证了"框架代码稳定不动、大依赖独立缓存、共享代码去重"。

注意:该策略只在 production 环境生效,开发模式不会触发,别在本地 dev 里验证分包效果。

第二步:把重型组件改成动态导入

分包策略解决的是"依赖"的去向,业务代码里体积最大的往往是重型组件:图表库、编辑器、报表页面。把它们从静态 import 改成 React.lazy(React 内置的按需加载 Hook),webpack 会在 import() 处自动切出一个独立 chunk:

import { lazy, Suspense } from 'react'; import BigReport from './BigReport'; // 静态写法:BigReport 及其引用的 chart 库会全部进主包 // import BigReport from './BigReport'; const BigReport = lazy(() => import('./BigReport')); export default function Page() { return ( // fallback 可替换为项目 loading 组件 <Suspense fallback={<div>loading...</div>}> <BigReport /> </Suspense> ); }

改哪里:只动 import 这一行和渲染处的 Suspense 包裹,组件本体不用改。 为什么:import() 是 webpack 识别的动态边界,被引用的 echarts、monaco 等大库会跟着这个 chunk 走,首屏不再背负它们。

适用场景:引用了 >100KB 第三方库的组件、非首屏的弹层/抽屉类重功能。首屏必经的核心组件不要拆,拆了反而多一次请求。

路由级的加载动画可以自定义:在项目约定目录放一个 loading.tsx,所有异步路由切换时都会渲染它,具体见官方目录结构文档。

第三步:用 ANALYZE 验证产物构成

改完配置不要凭感觉验收。构建时带上 ANALYZE 环境变量:

ANALYZE=1 pnpm build

构建完成后会弹出(或在 8888 端口)打开一个 bundle 分析面板,用 treemap 展示每个 chunk 的体积和内部模块占比。判断标准很简单:

  • umi.js 主文件占比是否明显下降;
  • 是否存在一个 1MB+ 的 vendors 巨块(说明选错了 bigVendors);
  • 同一份库代码是否散落在多个 chunk 里(说明共享模块没被合并)。

常见误区与避坑清单

  1. 在开发模式里看分包效果。codeSplitting 仅在 production 生效,dev 下 umi.js 的大小没有参考价值。
  2. 无脑上 depPerChunk。请求数会随依赖数量线性上涨,大型项目页面一开几十个小请求,反而拉高首屏 RTT 次数。官方结论是"无特殊场景,建议用 granularChunks",见配置文档。
  3. 拆了大组件却忘了 Suspense。lazy 组件必须包在 Suspense 里渲染,否则直接报错。fallback 用项目统一 loading 组件,体验才连贯。
  4. 只拆 JS 不看请求时序。拆得越碎,并行请求越多;在弱网/HTTP/1.1 环境下,20 个 50KB 文件的总耗时可能高于 1 个 1MB 文件。用 ANALYZE 面板 + 线上真实网络数据(如 Lighthouse 的 TBT 指标)双重确认。
  5. 把分包当万能药。压缩(服务器开启 gzip/brotli)、静态资源走 CDN、非关键脚本异步加载,这些和分包是互补关系。2.6MB 的 JS 经 gzip 后通常只剩 700~800KB,先确认服务器是否已开压缩,再评估分包收益。

落地顺序建议

按依赖关系排好执行顺序,每步可独立验证:

  1. 开启codeSplitting.jsStrategy: 'granularChunks',跑 ANALYZE 记录基线体积;
  2. 逐个改造引用大库的重型组件为 lazy 导入,每次改完重新构建对比;
  3. 配置服务器 gzip/brotli 压缩与 CDN;
  4. 用线上监控对比首屏 JS 体积与 LCP 变化,作为最终验收数据。

完整的路由拆包、loading 自定义与分包策略说明,可对照代码拆分博客 一文。分包不是越碎越好,目标是让"稳定的依赖"和"频繁变更的业务代码"物理分离——前者长期命中缓存,后者小步更新,这就是 umi.js 瘦身问题的完整解法。

【免费下载链接】umiA framework in react community ✨项目地址: https://gitcode.com/GitHub_Trending/um/umi

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询