Umi 4 常见问题实战指南从 dynamicImport 到构建优化的 22 个疑难杂症全解析【免费下载链接】umiA framework in react community ✨项目地址: https://gitcode.com/GitHub_Trending/um/umiUmi 4 在架构上相较于 Umi 3 发生了大量变化默认 React 18、默认开启 MFSU、基于 webpack 5 / Vite 双构建、内置代码按页拆分等。这些变化在带来更强能力的同时也催生了一批高频出现的配置与踩坑问题。本文以官方 FAQfaq.en-US.md为骨架逐条拆解 Umi 4 下关于代码分割、开发服务器、Less 编译、路由、HTML 模板、资源加载、压缩编码、多环境配置与浏览器兼容的 22 个典型问题并结合仓库源码说明底层原理帮你快速定位并解决问题。一、代码分割与加载体验dynamicImport 的关闭、loading 与分包策略1. 可以关闭 dynamicImport 吗可以但不建议。Umi 4 默认开启dynamicImport路由组件按需加载、按页拆包。若你的业务场景要求把 JS 产物全部打进单个umi.js例如某些轻量活动页、对首屏外链数量敏感的场景可以通过extraBabelPlugins在生产环境关闭动态导入安装依赖pnpm i babel-plugin-dynamic-import-node -D在.umirc.ts中配置且只针对 production 开启// .umirc.ts export default { extraBabelPlugins: process.env.NODE_ENV production ? [babel-plugin-dynamic-import-node] : [] }babel-plugin-dynamic-import-node会把import()语法在编译期转换为 CommonJS 的require从而让 webpack 无法再对路由进行代码分割。官方之所以不推荐是因为这会牺牲按需加载带来的首屏性能收益。注意该插件只在构建产物时生效开发环境保持动态导入以保留热更新能力。2. 没有 dynamicImport 时怎么配置它对应的 loading当你关闭dynamicImport或默认开启时可以通过约定文件src/loading.tsx定义路由懒加载过程中的全局 loading 组件。该文件导出的是 React 组件Umi 会在切换路由、加载 chunk 期间渲染它。从源码看Umi 在生成临时文件时会把扫描到的全局 loading 组件注入路由渲染逻辑packages/preset-umi/src/features/tmpFiles/tmpFiles.ts中存在loadingComponent: api.appData.globalLoading即该约定文件会被识别为globalLoading并作为路由加载组件使用。详细约定请参考 目录结构 loading.tsx。3. Umi 4 中如何进行代码分包Umi 4 默认按页拆包每个路由页面生成独立的 chunk只有访问到该页面时才加载对应 chunk。如果你觉得默认粒度还不够可以使用分包策略sub-package对业务模块进行进一步隔离手动拆分公共依赖例如把体积较大的第三方库单独提取。详见官方 代码拆分指南。而如果你有将所有 JS 产物打包成单个umi.js的需求则请按上文关闭dynamicImport。4._layout.tsx去哪了如何嵌套路由Umi 4 使用 react-router v6废弃了 Umi 3 时代的_layout.tsx约定。嵌套路由内容通过Outlet /组件渲染// layouts/index.tsx import { Outlet } from umi; export default function Layout() { return ( div h1公共布局/h1 Outlet / /div ); }父路由组件中放置Outlet /的位置就是子路由页面渲染的位置。相关约定详见 路由指南。二、React 版本与依赖兼容5. 可以使用 React 17 吗Umi 4 默认将 React 升级到了 v18使用 Umi 4 时需要注意依赖库与 React 18 的兼容情况。如果你确实需要降级到 React 17执行以下命令并重启即可pnpm add react^17 react-dom^17需要提醒的是React 18 引入了并发特性Concurrent Features部分依赖 React 17 生命周期语义的第三方库在 18 下可能异常同样降级到 17 也可能遇到依赖 React 18 新 API 的库报错务必评估好依赖矩阵后再降级。三、开发服务器与代理重启循环、SOCKET_SERVER 与 devServer 替代6. 静态资源代理到本地后页面一直 restart 刷新怎么办典型报错如下图所示Dev server disconnected. Polling for restart...开发服务器不断断开并轮询重启。解法配置SOCKET_SERVER环境变量指定 WebSocket 连接地址显式告诉客户端开发服务器监听的 socket 地址SOCKET_SERVERhttp://127.0.0.1:8000 pnpm dev底层原理SOCKET_SERVER是 Umi 开发服务器与浏览器客户端建立 HMR WebSocket 连接的关键环境变量。在 definePlugin.ts 中可以看到ENV_SHOULD_PASS白名单包含[NODE_ENV, HMR, SOCKET_SERVER, ERROR_OVERLAY]其中SOCKET_SERVER的处理逻辑是若当前存在process.env.SOCKET_SERVER则使用该值否则使用默认的 socket 地址。当你把静态资源或页面代理到本地其他端口、导致浏览器与 webpack dev server 之间 socket 地址不匹配时就会陷入断连重启循环此时显式指定SOCKET_SERVER即可修复。7. devServer 选项怎么配置Umi 4不再支持配置devServer选项但提供了两种替代方案使用proxy配置代理通过onProxyReq修改请求头信息。proxy配置的完整说明见 api/config proxy。// .umirc.ts export default { proxy: { /api: { target: http://localhost:3000, changeOrigin: true, onProxyReq(proxyReq) { // 修改请求头 proxyReq.setHeader(X-Custom-Header, value); }, }, }, };编写项目级插件通过插件向 dev server 插入 express 中间件实现对请求的灵活修改。项目级插件的写法见 使用插件 项目级插件。// plugin.ts import { IApi } from umi; export default (api: IApi) { api.addMiddlewares(() { return (req, res, next) { // 自定义中间件逻辑 next(); }; }); };四、样式与 Less 编译问题8.Error evaluating function round: argument must be a number怎么解决报错如下图所示通常是升级依赖后由新版 less 的行为变化引起的。原因新版 less 中/默认被识别为属性简写例如font: 12px/1.5中的/不再默认作为除法计算符号。于是类似round((...)/2)的写法中除法结果类型不符合预期抛出 argument must be a number。解法通过lessLoader恢复旧版行为将/默认用作计算符号// .umirc.ts export default { lessLoader: { math: always } };设置math: always后less 会像旧版一样总是把/当作数学运算符处理antd 等依赖旧版 less 除法语义的库即可正常编译。五、路由与运行时配置layout 迁移、base 与 pathname9. routes 里的 layout 配置选项不生效Umi 4 中layout 的运行时配置被移动到了app.ts即运行时配置文件不再放在路由配置里。在app.ts中通过layout导出项配置全局布局// app.ts export const layout { title: 我的应用, // 其他运行时布局配置 };完整可用的 layout 运行时配置项见官方 runtime-config 文档的layout一节。10.history中取的 pathname 为什么和useLocation中的不一样这种情况发生在项目配置了base时history.location.pathname取到的是浏览器地址栏中的 pathname它包含base如/my-app/pages/home路由相关 hooks如useLocation返回的是前端路由定义中的 pathname它不包含base如/pages/home。因此两者不一致是正常现象不要据此怀疑路由异常。路由 location 信息的详细说明见 路由指南 location 信息。六、HTML 模板与脚本注入11. document.ejs 去哪了如何自定义 HTML 模板Umi 4 移除了document.ejs约定自定义 HTML 产物有以下两种途径配置注入外部资源通过配置项注入外部 scripts 与 styles// .umirc.ts export default { scripts: [https://example.com/analytics.js], styles: [https://example.com/theme.css], };项目级插件通过插件 API 更灵活地修改 HTML 产物例如插入 meta 标签、改写模板结构。12. scripts 里配置的外部 js 为什么默认插入到 umi.js 的后面因为 React 只有在页面加载完毕后才会开始运行外部脚本插入到umi.js之后不会影响项目初始化同时可以保证外部脚本在 React 应用挂载后执行。若业务确实需要将外部脚本提前到umi.js之前需要调整 HTML 注入顺序或使用插件自定义模板。七、扩展语法与资源加载GraphQL、WebAssembly、自定义 loader 与 CSS Modules13. 怎么用 GraphQL需要为graph-ql文件配置对应的 loader。由于 Umi 4 使用 webpack 5 / Vite建议在chainWebpack中为.graphql/.gql扩展名添加自定义 loader或通过项目级插件扩展 webpack 配置实现。核心思路与下文自定义 loader一致。14. 怎么用 WebAssembly在.umirc.ts中通过chainWebpack开启 webpack 5 的 WebAssembly 实验特性并将.wasm从静态资源规则中排除单独建立异步 WebAssembly 规则// .umirc.ts export default { chainWebpack(config) { config.set(experiments, { ...config.get(experiments), asyncWebAssembly: true }) const REG /\.wasm$/ config.module.rule(asset).exclude.add(REG).end(); config.module .rule(wasm) .test(REG) .exclude.add(/node_modules/) .end() .type(webassembly/async) .end() }, }关键点有三处experiments.asyncWebAssembly: true开启 webpack 5 的异步 WebAssembly 支持从asset规则中排除.wasm避免被当作静态资源url/file/asset处理新增wasm规则并指定type: webassembly/async交给 webpack 的 WebAssembly 模块系统处理。15. 怎么自定义 loader根据场景不同通常需要先从静态资源规则中排除你要加载的文件类型再添加你自己的 loader或对现有规则进行修改。大致模板如下// .umirc.ts export default { chainWebpack(config) { // 1. 从现有 asset 规则中排除目标文件类型 config.module.rule(asset).exclude.add(/\.myext$/).end(); // 2. 添加自定义 loader 规则 config.module .rule(myext) .test(/\.myext$/) .use(my-loader) .loader(require.resolve(my-loader)) .end(); }, };16. 第三方包里如何使用 CSS Modules分两种情况第三方包直接发布源码直接将第三方包的jsx/ts/tsx源码发布到 npm无需转译为js。Umi 4 支持直接使用这类源码CSS Modules 自然生效。第三方包产物是js需要将其纳入 babel 额外处理才能支持 CSS Modules// .umirc.ts export default { extraBabelIncludes: [your-pkg-name] }extraBabelIncludes会将该包加入 babel 编译范围使其中的import ./style.less等样式引用能被 Umi 的 CSS Modules 管线正确处理。八、构建产物与压缩优化17. 如何调整产物的压缩编码格式默认 js / css 的压缩器esbuild采用ascii格式编码压缩这会导致中文字符被转码为\uXXXX形式增大产物体积。可通过配置将编码调整为utf8防止字符被转换// .umirc.ts export default { jsMinifierOptions: { charset: utf8 }, cssMinifierOptions: { charset: utf8 } }或者直接切换压缩器// .umirc.ts export default { jsMinifier: terser, cssMinifier: cssnano }对比esbuild压缩速度极快但默认ascii编码会膨胀中文产物tersercssnano是更经典的压缩方案字符处理更保守。选择哪个取决于你对构建速度与产物体积的权衡。18. npm link 的包不热更新怎么解决Umi 4 默认开启mfsu模块联邦秒开方案而mfsu默认忽略node_modules的变化。通过npm link链接到node_modules的本地包其文件变更不会被监听导致热更新失效。解法是把该包从mfsu中排除// .umirc.ts export default { mfsu: { exclude: [package-name] }, }排除后该包将走常规编译路径本地修改即可触发热更新。mfsu的更多说明可参考 mfsu 独立使用。九、多环境配置与浏览器兼容19. 多环境 config 文件的优先级是怎样的Umi 通过UMI_ENV环境变量区分多环境配置。加载优先级请参考 环境变量 UMI_ENV无论使用config/config.ts还是.umirc.ts规则一致UMI_ENV指定的环境配置会覆盖基础配置。20. IE 兼容性问题在当下现代浏览器主流背景下Umi 4默认不兼容 IE。若你有调整构建兼容目标、兼容非现代浏览器或兼容 IE 的需求请参考 非现代浏览器兼容指南通过调整构建目标的 browserslist 配置实现降级兼容。21. SSR 问题SSR 目前仍是实验性特性官方不建议在生产环境使用。若在 SSR 使用中发现问题请在 Umi 的 issue 区反馈。22. Vue / Vite 问题Umi 4 新增了 Vite 模式和 Vue 支持可参考仓库中的examples/vue-demo、examples/bundler-vite-demo等示例。由于这两项能力相对较新可能存在边缘情况edge case遇到问题可在 issue 区反馈。小结Umi 4 的这批高频 FAQ 覆盖了从要不要关 dynamicImport到压缩编码怎么调的完整链路背后其实对应着 Umi 4 的几大架构决策默认按页拆包dynamicImport、默认 MFSUnpm link 问题根源、webpack 5WebAssembly 配置方式、react-router v6Outlet /嵌套、React 18版本降级、以及 esbuild 默认压缩中文转码问题。理解这些底层机制后再遇到类似报错时就能迅速定位到对应配置项。更多配置项细节可查阅 config 配置文档 与 运行时配置文档。【免费下载链接】umiA framework in react community ✨项目地址: https://gitcode.com/GitHub_Trending/um/umi创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考