前言
Umi,是 可扩展的
企业级前端应用框架。同时支持 配置式路由
和 约定式路由
,保证路由的功能完备,并以此进行功能扩展。然后配以生命周期完善的插件体系
,覆盖从源码到构建产物的每个生命周期,支持各种功能扩展和业务需求。
本文将从以下几个角度介绍 Umi 的特性:
- 开箱即用
- 约定化思想
- 路由和导航
- 运行时配置 & 非运行时配置
- 插件扩展 & Umi Max
- 性能优化
本文创作于2023年10月,创作时的 Umi 版本为 4.0.84
Umi 官方文档:umijs.org/docs/introd...
开箱即用
使用 create-react-app
搭建项目时,通常需要手动安装 React Router
、Redux 等状态管理容器
、配置 TypeScript
、Less
等。
Umi 实现了 "import all from umi"
的设计思路,即所有的 import 都可以来自 umi。如 useNavigate 不是 import { useNavigate } from 'react-router'
,而是 import { useNavigate } from 'umi'
,从 umi 中导出。
导出的方法不仅来自 umi 自身,还来自 umi 插件。当使用了 umi 插件时,插件中导出的内容也可以从 umi 导入。如 import { useRequest } from 'umi'
使用了 Umi Max 插件中的方法。
Umi 通过内部的实践和讨论以及社区得出了一些最佳实践。如 路由
、补丁方案
、数据流
、请求
、权限
、国际化
、微前端
、icons 使用
、编辑器使用
、图表
、表单
等方面。
约定化思想
Umi 的许多功能设计上使用了 基于目录
的约定化设计,你只需要按照约定的目录名称在指定位置创建目录即可实现对应的功能。
如:
- src/layout - 布局文件
- src/pages - 页面
- src/models - 数据流模式
- mock - API 模拟
- locales - 国际化功能
- access.ts - 权限模型
除了
基于目录
的约定式设计,Umi 具有另外两种配置方式:
- 运行时配置 - .umirc.ts
- 非运行时配置 - app.ts
路由和导航
配置式路由
直接给出一个配置文件,讲解一下与路由相关的内容
TypeScript
export default {
routes: [
{
path: '/login', component: 'login'
},
{
path: '/test',
component: '@/layouts/index',
routes: [
{ path: '/list', component: 'list' },
{ path: '/admin', component: 'admin' },
],
},
{
path: '/home', redirect: '/'
}
{
path: '/user',
component: 'user',
wrappers: [
'@/wrappers/auth',
]
}
],
}
path
路由的路径 Umi 中的路由路径仅支持两种动态占位符:
:
路由变量*
路径通配符
以下是一些有效配置:
bash
/groups
/groups/admin
/users/:id
/users/:id/messages
/files/*
/files/:id/*
component
Umi 按目录结构搜索组件,默认会在 src/pages
中搜索组件。
如:component: 'user'
代表 src/pages/user/index.tsx
。
如果要明确指定 src 目录中的文件,可以使用 Webpack alias
,如:@/layouts/basic
。
routes
以上示例中给出了页面含有子路由的情况,/test/list
/test/admin
。
使用子路由时的目录结构:
bash
src/pages/test/pages/list/index.tsx
src/pages/test/pages/admin/index.tsx
如示例所示,子页面与父页面可以拥有相同的目录结构,在父页面文件夹中创建 pages
文件夹,然后把子页面放在文件夹中,可以实现子页面。
在父页面中,通过 React-Router 6 的 <Outlet />
组件显示子页面的内容:
TypeScript
const Parent = () => {
return <div>
<Outlet /> <!-- 子路由的内容会显示在这里 -->
</div>
}
wrappers
Umi 约定使用 wrapper
包裹页面实现 逻辑层面
的容器,如 页面权限校验
;使用 Layout
包裹页面实现 视图层面
的容器,如:公用 Header、Footer
。
示例中 /user
路由添加了一个 wrapper
,它的内部是这样实现的:
TypeScript
import { Navigate, Outlet } from 'umi';
export default (props) => {
const { isLogin } = useAuth();
if (isLogin) {
return <Outlet />;
} else {
return <Navigate to="/login" />;
}
}
约定式路由
动态路由
Umi 使用了 基于目录和文件名
的约定式路由。
约定,带 $
前缀的目录或文件为动态路由。若 $
后不指定参数名,则代表 *
通配,比如以下目录结构:
src/pages/users/$id.tsx
会成为/users/:id
src/pages/users/$id/settings.tsx
会成为/users/:id/settings
举个完整的例子,比如以下文件结构:
bash
└── pages
├── 404.tsx
├── index.tsx
└── users.tsx
会生成以下路由结构:
javascript
[
{ path: '/', component: '@/pages/index.tsx' },
{ path: '/foo/:slug', component: '@/pages/foo/$slug.tsx' },
{ path: '/:bar/*', component: '@/pages/$bar/$.tsx' }
]
404 路由
约定 src/pages/404.tsx
为 404 页面,需返回 React 组件。
生成对应的路由:
javascript
[
{ path: '/404', component: '@/pages/404.tsx' },
]
Loading 页面
Umi 4 默认按页拆包,从而有更快的页面加载速度,由于加载过程是异步的,所以往往你需要编写 loading.tsx
来给项目添加加载样式,提升体验。
运行时配置 & 非运行时配置
Umi 中的模块采用了高度配置化的设计,提供了两种配置方式:
- 非运行时配置 - 在本地的配置文件中配置,构建时生效
- 运行时配置 - 运行在浏览器端的配置
非运行时配置
非运行时配置是指在应用 构建阶段
确定的配置项,无法在应用运行时动态修改。
UmiJS 的非运行时配置主要包括以下几个方面:
-
构建配置:在构建阶段,可以通过配置来指定构建输出的目录、静态资源路径、打包方式等。UmiJS 提供了默认的构建配置,也支持根据需求进行自定义。
-
环境变量配置:通过环境变量配置,可以在不同环境下设置不同的参数。例如,在开发环境和生产环境中使用不同的接口地址、API 密钥等。
-
主题配置:UmiJS 支持通过配置主题来定制应用的样式。可以通过设置颜色、字体等属性来实现全局样式的统一管理。
-
路由配置:可以在
.umirc.js
中配置路由信息。
非运行时配置文件:
.umirc.ts
运行时配置
运行时配置是指在应用运行时可以动态修改的配置项。UmiJS 的运行时配置包括以下几个方面:
-
路由配置:通过配置路由,可以定义应用的页面结构和导航规则。UmiJS 提供了灵活的路由配置方式,支持配置式路由和约定式路由两种模式。
-
插件配置:UmiJS 的插件系统可以通过配置来启用、禁用和自定义各种插件。通过插件配置,可以扩展 UmiJS 的功能,例如添加自定义的 webpack 配置、引入第三方库等。
-
代理配置:在开发环境中,UmiJS 可以通过代理配置实现跨域请求。通过配置代理,可以将请求转发到指定的目标服务器,解决前端开发中的跨域问题。
约定
src/app.tsx
为运行时配置
插件扩展 & Umi Max
Umi 的核心就在于它的插件机制。基于 Umi 的插件机制,你可以获得扩展项目的 编译时
和 运行时
的能力。
Umi Max 是由 Umi 官方开发的 插件集
, Max 内置了以下插件:
- 权限
- 站点统计
- Antd
- 图表
- dva
- initial-state
- 数据流
- 布局和菜单
- 国际化(多语言)
- model
- 乾坤微前端
- 请求库
- Tailwind CSS
- CSS-IN-JS
- 请求方案
- 全局数据存储方案
- Module Federation
项目级插件
当你有 Umi 定制需求时,可以创建 src/plugin.ts
。该插件仅在项目内部生效