4 个代码分割手段彻底拆掉 umi 首屏大包【免费下载链接】umiA framework in react community ✨项目地址: https://gitcode.com/gh_mirrors/umi8/umi首屏 JS 跑到 1.4MB白屏就不只是几秒的问题了。umi build跑完后ls -lh dist/static你会发现共享依赖全部塞进了umi.xxxx.js主包页面只是分了几个小 chunk。本文基于 umi 的代码分割机制讲 4 个实战手段核对路由切分、组件懒加载、拆 vendor、大库外置并教你用 ANALYZE 报告量化每一步的效果。你将学到读懂dist产物结构确认每个路由页面是否真的输出了独立的*.async.js用umi/dynamic写组件级懒加载避开对象签名的常见误用用commons把第三方依赖拆成vendors用externals把大库交给 CDN用ANALYZE报告与产物体积对比量化验证瘦身效果判断publicPath、disableDynamicImport等配置的适用边界 看构建产物确认 umi 路由切分是否真的生效这一节解决不清楚当前包胖在哪路由切分到底替你做了什么的问题。umi 根据pages目录约定生成路由docs/guide/router.md构建时默认把每个路由输出为独立 chunk。命名规则在 packages/af-webpack/src/getConfig.js 的 output 部分// packages/af-webpack/src/getConfig.js 中的 output 相关逻辑节选 // 仅生产环境且开启 hash 时文件名才带 8 位 chunkhash const jsHash !isDev opts.hash ? .[chunkhash:8] : ; filename: [name]${jsHash}.js, // 首屏入口 chunkFilename: [name]${jsHash}.async.js, // 路由/动态导入的异步 chunk注意jsHash受isDev和opts.hash双重限制所以开发模式下、或未开hash: true的构建产物里你看到的是不带哈希的干净文件名。用两条命令人工核对路由切分# 构建后查看 chunk 输出结构 $ umi build $ ls dist/static产物形态参考 docs/guide/deploy.md 中的示例./dist ├── index.html └── static ├── pages__index.5c0f5f51.async.js # 首页 chunk ├── pages__list.f940b099.async.js # 列表页 chunk ├── umi.2eaebd79.js # 首屏入口 └── umi.f4cb51da.cssindex.html只引用umi.{hash}.js与umi.{hash}.css两个pages__*.async.js在访问对应路由时才下载。你可以进一步验证在pages下新增foo.js重新构建static目录里应出现对应的pages__foo.xxx.async.js。如果新页面代码进了入口包说明约定被破坏常见原因是 layout 或入口文件里直接静态 import 了页面组件。 用 umi/dynamic 写对组件级代码分割这一节解决页面内部的大组件如何拆出去以及 API 怎么写才不出错的问题。dynamic 的签名是函数不是对象import dynamic from umi/dynamic实际通过 alias 指向 packages/umi/src/dynamic.js见 packages/umi/src/buildDevOpts.js 中的libraryAlias。它接收的第一个参数是返回 Promise 的函数第二个参数是选项——直接照搬其他库的对象写法是最常见的翻车点// ❌ react-loadable 风格的对象签名umi/dynamic 不接受 const Chart dynamic({ loader: () import(./Chart), }); // ✅ 第一个参数是返回 Promise 的函数第二个参数是选项 const Chart dynamic(() import(./Chart), { loading: () div加载中.../div, });loading 状态与卸载保护实现上dynamic是一个包装类组件构造函数里就调用resolvePromise 未 resolve 时渲染LoadingComponentresolve 后setState渲染目标组件内部用mounted标记避免组件卸载后再 setState。因此有两点值得注意loading只在请求未返回时渲染适合放骨架屏如果你需要控制加载但暂不渲染要自己通过hoc选项包一层。官方最小示例来自 docs/guide/load-on-demand.md实际业务中写成这样即可import dynamic from umi/dynamic; // 图表只在页面下半部分使用拆出去不占首屏 const ChartPanel dynamic( () import(./ChartPanel), { loading: () div图表加载中.../div, } ); export default function Dashboard() { return ( div OrderList / ChartPanel / /div ); }此时图表代码及其依赖进入独立异步 chunkDashboard 首屏只加载它此刻需要的部分。 用 commons 和 externals 把第三方依赖拆出去这一节解决node_modules 共享代码全进了首屏入口单个大库抬高整个首屏的问题。用 commons 拆出 vendor chunkaf-webpack 会把commons数组的每一项转成CommonsChunkPluginpackages/af-webpack/src/getConfig.js 第 245 行附近。配置写在.webpackrc或.webpackrc.js配置项清单见 packages/af-webpack/Configuration.md// .webpackrc.js可直接粘贴按需逐项开启 export default { hash: true, // 开启后文件名带 8 位 chunkhash长效缓存的前提 // 把 node_modules 全部归入 vendors chunk commons: [ { name: vendors, minChunks: ({ resource }) resource resource.indexOf(node_modules) -1, }, ], // 把 echarts 交给 CDN不进 bundle externals: { echarts: echarts, // 对应全局变量 window.echarts }, };拆完之后第三方库集中在vendorschunk每个页面的异步 chunk 只剩业务代码——改页面不会让 vendors 的缓存失效。externals接受普通对象或函数packages/af-webpack/src/getUserConfig/configs/externals.js对 echarts 这类单一大库外置比留在 vendors 里更彻底。大库的 CDN 脚本从哪来externals只负责不打包脚本本身要在 HTML 里注入。umi 约定新建pages/document.ejs作为模板docs/guide/html-template.mddiv idroot/div script src/static/vendor/echarts.min.js/script把echarts.min.js放进public/目录构建时会被拷贝到dist。另外如果项目用了 moment在.webpackrc加ignoreMomentLocale: trueaf-webpack 会用 webpack 的 IgnorePlugin 排除 moment 的全部 locale 文件packages/af-webpack/src/getConfig.js 第 610 行附近这是中后台项目里便宜见效的一刀。 用 ANALYZE 报告与产物体积量化代码分割效果这一节解决配置改完了怎么证明它变小、小了多少的问题。启动 ANALYZE 报告并记录体积基线af-webpack 内置BundleAnalyzerPlugin由ANALYZE环境变量控制# 构建后启动本地 server默认 8888 端口并自动打开浏览器 $ ANALYZE1 umi build # 改端口 $ ANALYZE1 ANALYZE_PORT9999 umi build # 记录主包体积与总体积作为对比基线 $ ls -lh dist/static | head -n 20 $ du -sh dist/static报告里能直接看到每个 chunk 的体积、入口 chunk 里还混了哪些 node_modules 模块配合上面的两条命令改动前后的数据就能落在同一张表里。四条判断标准主入口umi.*.js在 vendors 拆出后体积应明显下降首屏总下载量 入口 vendors如有 首页对应 async chunk改动前后分别加总对比pages__*.async.js的数量与pages里的路由数对应缺了就查路由约定是否被破坏开了hash: true后浏览器重复访问确认 vendors chunk 命中 304说明长效缓存真正生效。如果报告里 node_modules 模块仍然挂在入口 chunk 下说明 commons 没生效优先检查.webpackrc是否写错位置、minChunks函数是否对目标路径返回了true。⚠️ 代码分割的常见坑与适用边界这一节说明哪些手段在特定场景下会起反作用。三个容易误用的场景静态资源部署到 CDN 却没配publicPath异步 chunk 由浏览器按publicPath请求只把dist/static上传 CDN 而不配publicPath路由切换时会 404且控制台往往没有明显报错。用.webpackrc的publicPath或PUBLIC_PATH环境变量解决详见 docs/guide/deploy.md。SSR / Node 运行时Node 侧不支持import()。umi 提供disableDynamicImport配置af-webpack 会为此追加babel-plugin-dynamic-import-node-sync把动态导入降级为同步 requirepackages/af-webpack/src/getConfig.js 第 282 行附近。有服务端渲染的场景不配它运行时会直接报错。过度分割大量小组件都套dynamic会产出几十个 10KB 以下的小 chunk请求次数开销可能超过体积收益首屏就要渲染的组件也不该懒加载。用 ANALYZE 报告判断若异步 chunk 普遍极小且数量庞大考虑合并。接下来可以逐行读一遍 packages/af-webpack/src/getConfig.js找到你业务里最需要的那组插件如果更关心 dev 启动速度再看 packages/umi-plugin-dll 的 DLL 思路。【免费下载链接】umiA framework in react community ✨项目地址: https://gitcode.com/gh_mirrors/umi8/umi创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考