umi4.js详解

1.什么是umi?

Umi,中文发音为「乌米」,是可扩展的企业级前端应用框架。Umi 以路由为基础,同时支持配置式路由和约定式路由,保证路由的功能完备,并以此进行功能扩展。然后配以生命周期完善的插件体系,覆盖从源码到构建产物的每个生命周期,支持各种功能扩展和业务需求。

2.umi的特性有哪些?

  1. 路由驱动:通过路由配置生成应用结构,支持动态路由、嵌套路由、权限路由等。
  2. 插件系统:通过插件扩展功能(如国际化、代码分割、API 请求、Ant Design 集成等)。
  3. 配置化:通过 config/config.js 或 .umirc.ts 集中配置,灵活且可扩展。
  4. 按需加载:支持代码分割和懒加载,提升应用性能。
  5. 内置开发工具:提供热更新、路由跳转、插件调试等功能。

3.umi的优点 

1、企业级,在安全性、稳定性、最佳实践、约束能力方面会考虑更多
2、插件化,啥都能改,Umi 本身也是由插件构成
3、MFSU,比 Vite 还快的 Webpack 打包方案
4、基于 React Router 6 的完备路由
5、默认最快的请求
6、SSR & SSG
7、稳定白盒性能好的 ESLint 和 Jest
8、React 18 的框架级接入
9、Monorepo 最佳实践 

4.umi的基本使用  

1. 安装与初始化

首先得有 node,并确保 node 版本是 18 或以上

然后需要包管理工具。node 默认包含 npm,但也可以选择其他方案,

创建项目:

pnpm dlx create-umi@latest  my-app
✔ Install the following package: create-umi? (Y/n) · true
✔ Pick Npm Client › pnpm
✔ Pick Npm Registry › taobao
Write: .gitignore
Write: .npmrc
Write: .umirc.ts
Copy:  layouts/index.tsx
Write: package.json
Copy:  pages/index.tsx
Copy:  pages/users.tsx
Copy:  pages/users/foo.tsx
@ postinstall /private/tmp/sorrycc-vylwuW
umi setup
info  - generate files

//国内建议选 pnpm + taobao 源,速度提升明显。这一步会自动安装依赖,同时安装成功后会自动执行 umi setup 做一些文件预处理等工作。
/*
执行之后会在控制台会出现用户交互操作,按照提示来操作即可。
第一步是让你确定项目文件夹的名称,然后选择项目的模版。因为在Umi3中是没有集成一些插件的,很多的插件在项目创建之后在根据需要进行安装。但是Umi4变得更加方便了,直接在项目中集成插件,然后根据需要进行启用和关闭。
*/

 模板选项

  • Simple App:基础项目模板,纯净版的Umi,包含umi4文档上关于Guides下的所有功能,但是不包括Umi Max。。
  • Ant Design Pro:集成 Pro Layout 和权限系统的完整模板 ,包含Umi Max的完整功能,均可通过在.umirc.ts 或 config/config.ts中插拔式配置

2.启动项目

执行 pnpm dev 命令,

pnpm dev
        ╔═════════════════════════════════════════════════════╗
        ║ App listening at:                                   ║
        ║  >   Local: https://127.0.0.1:8000                  ║
ready - ║  > Network: https://192.168.1.1:8000                ║
        ║                                                     ║
        ║ Now you can open browser with the above addresses👆 ║
        ╚═════════════════════════════════════════════════════╝
event - compiled successfully in 1121 ms (388 modules)
event - MFSU compiled successfully in 1308 ms (875 modules)

 在浏览器里打开 http://localhost:8000/,能看到以下界面,

 3.启用 Prettier(可选)

 如果需要用 prettier 做项目代码的自动格式化,执行 pnpm umi g

pnpm umi g
✔ Pick generator type › Enable Prettier -- Enable Prettier
info  - Write package.json
info  - Write .prettierrc
info  - Write .prettierignore
info  - Install dependencies with pnpm

4.umi的目录结构

这里罗列了 Umi 项目中约定(或推荐)的目录结构,在项目开发中,请遵照这个目录结构组织代码。

├── config/              // 配置目录(核心)
│   └── config.ts        // 主配置文件(支持多环境配置)
├── dist/                // 构建产物目录(默认输出路径)
├── mock/                // 模拟数据目录
│   └── app.ts|tsx       // Mock 数据定义入口
├── src/                 // 源码核心目录(开发主区域)
│   ├── .umi/            // 临时文件目录(框架生成,勿手动修改)
│   ├── .umi-production/ // 生产环境临时文件(构建时生成)
│   ├── layouts/         // 全局布局目录(约定式)
│   │   ├── BasicLayout.tsx  // 基础布局组件
│   │   └── index.less       // 布局样式
│   ├── pages/           //页面组件目录(约定式路由核心)
│   │   ├── index.less   // 页面样式
│   │   └── index.tsx    // 页面组件(自动映射为路由)
│   ├── models/          // 数据模型目录(推荐)
│   ├── utils/           // 工具函数目录(推荐)
│   ├── services/        //  服务目录api接口配置(推荐)
│   ├── app.tsx          // 运行时配置入口(扩展生命周期)
│   └── global.less      // 全局样式覆盖
└── public/              // 静态资源目录(直接拷贝至构建产物)
    └── favicon.ico      // 网站图标(需与配置路径匹配)

1. 目录结构详解

package.json:与 Umi 3 不同,Umi 4 不会自动注册 package.json 中以 @umijs/preset-@umijs/plugin-umi-preset- 和 umi-plugin- 开头的插件、预设,若你需要自定义额外的插件、预设,需要手动配置到 plugins 。

.umirc.ts:与 config/config.ts 文件功能相同,2 选 1 。.umirc.ts 文件优先级较高,区别是你可以单独在一个 config 文件夹下集中管理所有的配置,保持项目根目录整洁。包含 Umi 所有非运行时配置(运行时配置一般定义于 app.ts

dist 目录:执行 umi build 后产物的默认输出文件夹。可通过 outputPath 配置修改产物输出文件夹。

mock 目录:存放 mock 文件,此目录下所有 .ts / .js 文件会被 mock 服务加载,从而提供模拟数据,使用方法详见 Mock 。

public 目录:存放固定的静态资源,如存放 public/image.png ,则开发时可以通过 /image.png 访问到,构建后会被拷贝到输出文件夹。

.umi 目录.umi/ 为临时目录,禁止手动修改(需加入 .gitignore)。默认已在 .gitignore 被忽略dev 时的临时文件目录,比如入口文件、路由等,都会被临时生成到这里。

.env:环境变量文件(如 API_URL=http://api.example.com

app.[ts|tsx]:运行时配置文件,可以在这里扩展运行时的能力,比如修改路由、修改 render 方法等。运行时配置带来的逻辑会在浏览器中运行,因此当有远程配置、动态内容时,这些我们在本地开发时还不确定,不能写死,所以需要在浏览器实际运行项目时动态获取他们。

// 示例:全局错误处理  
export function onError(error: Error) {  
  console.error('全局错误:', error);  
}  

 global.(j|t)sx:全局前置脚本文件。Umi 区别于其他前端框架,没有显式的程序主入口(如 src/index.ts),所以当你有需要在应用前置、全局运行的逻辑时,优先考虑写入 global.ts 。

//‌全局数据共享与状态管理‌:通过 useModel 实现跨组件的数据共享,替代部分 dva 场景
// src/models/global.tsx  
import { useState } from 'react';  
export default () => {  
  const [globalData, setGlobalData] = useState({});  
  return { globalData, setGlobalData };  
};  
文件作用域典型场景
app.ts运行时环境(应用实例)全局状态初始化、路由拦截、错误边界处理
global.tsx全局数据共享或类型声明跨组件状态管理、全局类型定义

 当你需要添加全局 Context 、修改应用运行时,请使用 app.tsx 。 

2.注意

config/ 和 src/pages/ 为框架识别目录,不可更名或删除。

 可新增 src/components/src/hooks/ 等目录管理公共组件或逻辑。

 通过 app.tsx 扩展运行时配置(如权限拦截、全局状态注入)

5.umi的环境变量  

1. 基础环境变量

  • 定义方式
    在项目根目录创建 .env 文件,格式为 KEY=VALUE

  • .env:基础配置(所有环境生效)
  • .env.local:本地覆盖配置(优先级高于.env
  • .env.production:生产环境专用变量

支持变量嵌套(如CONCAT=$FOO$BAR)  

# .env
API_URL=http://api.example.com
NODE_ENV=development
  •  使用方式

在代码中通过 process.env.KEY 访问:

// src/services/api.ts
const BASE_URL = process.env.API_URL;

2.‌多环境配置文件

通过 UMI_ENV 指定环境,加载对应配置:

//启动预发布环境
 UMI_ENV=staging pnpm dev

对应加载文件优先级:config.ts → config.${UMI_ENV}.ts → config.${dev|prod|test}.ts → config.local.ts

 3.运行时变量注入

config/config.ts中使用define字段暴露变量至前端代码:

前端通过process.env.API_SECRET读取值(需注意敏感信息需加密处理)

export default defineConfig({
  define: {
    'process.env.UMI_ENV': process.env.UMI_ENV || 'dev',
    'process.env.API_URL': '"https://prod.example.com"', // 需要转义双引号
    'process.env.API_SECRET': 'your_key_here'
  }
});

 4. 构建时注入变量

通过命令行动态覆盖环境变量:

在启动或构建命令中直接传递变量,优先级最高,用于临时调试场景(如快速修改开发服务器端口) :

API_URL=http://test.example.com umi build
// 修改端口示例(跨平台推荐 cross-env)
cross-env PORT=3000 pnpm dev

 5.多环境管理规则

  1. 保留名称限制
    dev/test/prod为框架保留值,手动设置无效,需改用自定义名称(如stagingpre)触发多环境配置加载。

  2. 配置文件示例
    开发环境配置(config/config.staging.ts):

export default defineConfig({
  define: {
    'process.env.API_HOST': 'https://staging.api.com'
  },
  proxy: { '/api': { target: 'http://staging.server' } }
});

6.umi的约定式路由

1.基本规则
1.文件路径映射路由

Umi4 根据 src/pages 目录下的文件结构自动生成路由配置:

pages/
├── index.tsx → path: '/'
├── about.tsx → path: '/about'
└── users/
    └── index.tsx → path: '/users'
2.嵌套路由
子目录会生成嵌套路径:
pages/
└── users/
    ├── index.tsx → path: '/users'
    └── list.tsx → path: '/users/list'

 生成的路由配置为:

[
  { path: '/users', component: '@/pages/users/index' },
  { path: '/users/list', component: '@/pages/users/list' },
]
 3.动态路由

动态参数路由
使用 [参数名] 包裹文件或目录,生成动态参数路径,组件内通过 props.match.params 获取参数::

pages/
└── users/
    └── [id].tsx → path: '/users/:id'
// /pages/users/[id].jsx
export default (props) => {
  const { id } = props.match.params;  // 访问 /users/123 时 id=123
  return <div>{id}</div>;
};

 可选动态参数路由
使用 [参数名$] 标记为可选参数:

//在动态路由定义中,/pages/user/[id$].jsx 表示 id 为可选,访问 /user 和 /user/123 均可匹配该路由
pages/
└── users/
    └── [id$].tsx → path: '/users/:id?'
 4. 嵌套路由与布局

局部嵌套路由
在目录中添加 _layout.tsx 文件,作为该目录的布局容器,并通过 props.children 渲染子路由:

pages/
└── users/
    ├── _layout.tsx → 父路由布局
    ├── index.tsx → path: '/users'
    └── detail/[id].tsx → path: '/users/detail/:id'

 生成的路由配置为:

{
  path: '/users',
  component: '@/pages/users/_layout',
  routes: [
    { path: '/users', component: '@/pages/users/index' },
    { path: '/users/detail/:id', component: '@/pages/users/detail/[id]' },
  ],
}

 全局布局
在 src/layouts/index.tsx 定义全局布局,包裹所有路由组件。

[
  { exact: false, path: '/', component: '@/layouts/index',
    routes: [
      { exact: true, path: '/', component: '@/pages/index' },
      { exact: true, path: '/users', component: '@/pages/users' },
    ],
  },
]
// /src/layouts/index.jsx
export default ({ children }) => {
  return (
    <div>
      <Header />
      {children}
      <Footer />
    </div>
  );
};

不同的全局布局

你可能需要针对不同路由输出不同的全局 layout,Umi 不支持这样的配置,但你仍可以在 src/layouts/index.tsx 中对 location.path 做区分,渲染不同的 layout 。

比如想要针对 /login 输出简单布局

export default function(props) {
  if (props.location.pathname === '/login') {
    return <SimpleLayout>{ props.children }</SimpleLayout>
  }

  return (
    <>
      <Header />
      { props.children }
      <Footer />
    </>
  );
}
5.404 页面

在 src/pages/404.tsx 中定义自定义 404 页面,开发模式下需通过 /404 显式访问。

 注意:以下文件或目录会被 忽略,不生成路由

  • 以 . 或 _ 开头的文件或目录
  • 以 d.ts 结尾的类型定义文件
  • 以 test.tsspec.tse2e.ts 结尾的测试文件(适用于 .js.jsx 和 .tsx 文件)
  • components 和 component 目录
  • utils 和 util 目录
  • 不是 .js.jsx.ts 或 .tsx 文件
  • 文件内容不包含 JSX 元素

7 umi的路由 

 若配置文件(.umirc.ts 或 config/config.ts)中定义了 routes,则 禁用约定式路由,仅使用配置路由。

 在配置文件中通过 routes 进行配置,格式为路由信息的数组。

// .umirc.ts
export default {
  routes: [
    { path: '/', component: 'index' },
    { path: '/user', component: 'user' },
  ],
}

 Umi 4 默认按页拆包,从而有更快的页面加载速度,由于加载过程是异步的,所以往往你需要编写 loading.tsx 来给项目添加加载样式,提升用户体验。

 1.属性详解:
  •  pathpath 只支持两种占位符配置,第一种是动态参数 :id 的形式,第二种是 * 通配符,通配符只能出现路由字符串的最后。
  • component:配置 location 和 path 匹配后用于渲染的 React 组件路径。可以是绝对路径,也可以是相对路径。如果是相对路径,会从 src/pages 开始寻找。如果指向 src 目录的文件,可以用 @,比如 component: '@/layouts/basic',推荐使用 @ 组织路由文件位置。
  • routes:配置子路由,通常在需要为多个路径增加 layout 组件时使用。
//这样,访问 /list 和 /admin 就会带上 src/layouts/index 这个 layout 组件。

export default {
  routes: [
    { path: '/login', component: 'login' },
    {
      path: '/',
      component: '@/layouts/index',
      routes: [
        { path: '/list', component: 'list' },
        { path: '/admin', component: 'admin' },
      ],
    }, 
  ],
}

 在全局布局 src/layouts/index 中,通过 <Outlet/> 来渲染子路由

  • redirect:配置路由跳转。

//访问 / 会跳转到 /list 。
export default {
  routes: [
    { path: '/', redirect: '/list' },
    { path: '/list', component: 'list' },
  ],
}

 重定向时,默认不会携带原 url 的查询参数,如需保持原参数,添加 keepQuery 选项即可:

  routes: [
    { path: '/', redirect: '/list', keepQuery: true },

    // 注:若你需在跳转时处理参数,可以自行实现一个跳转组件
  ]
  •  wrappers:配置路由组件的包装组件,通过包装组件可以为当前的路由组件组合进更多的功能。 比如,可以用于路由级别的权限校验:
export default {
  routes: [
    { path: '/user', component: 'user',
      wrappers: [
        '@/wrappers/auth',
      ],
    },
    { path: '/login', component: 'login' },
  ]
}

 然后在 src/wrappers/auth 中,

import { Navigate, Outlet } from 'umi'

export default (props) => {
  const { isLogin } = useAuth();
  if (isLogin) {
    return <Outlet />;
  } else{
    return <Navigate to="/login" />;
  }
}

 这样,访问 /user,就通过 auth 组件做权限校验,如果通过,渲染 src/pages/user,否则跳转到 /login 

layout:通过配置 layout: false 可以单独关闭某一个路由的全局布局

layout: false 仅对一级路由生效

// .umirc.ts

export default {
  routes: [
    // 取消 login 页面的全局布局,从而自行实现整个页面
    { path: '/login', component: '@/pages/Login', layout: false },
  ],
}