1.什么是umi?
Umi,中文发音为「乌米」,是可扩展的企业级前端应用框架。Umi 以路由为基础,同时支持配置式路由和约定式路由,保证路由的功能完备,并以此进行功能扩展。然后配以生命周期完善的插件体系,覆盖从源码到构建产物的每个生命周期,支持各种功能扩展和业务需求。
2.umi的特性有哪些?
- 路由驱动:通过路由配置生成应用结构,支持动态路由、嵌套路由、权限路由等。
- 插件系统:通过插件扩展功能(如国际化、代码分割、API 请求、Ant Design 集成等)。
- 配置化:通过
config/config.js
或.umirc.ts
集中配置,灵活且可扩展。 - 按需加载:支持代码分割和懒加载,提升应用性能。
- 内置开发工具:提供热更新、路由跳转、插件调试等功能。
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.多环境管理规则
-
保留名称限制
dev/test/prod
为框架保留值,手动设置无效,需改用自定义名称(如staging
、pre
)触发多环境配置加载。 -
配置文件示例
开发环境配置(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.ts
、spec.ts
、e2e.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.属性详解:
- path:
path
只支持两种占位符配置,第一种是动态参数: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 },
],
}