路由
在 Umi
应用是单页应用,页面地址的跳转都是在浏览器端完成的,不会重新请求服务端获取 html,html 只在应用初始化时加载一次。所有页面由不同的组件构成,页面的切换其实就是不同组件的切换,你只需要在配置中把不同的路由路径和对应的组件关联上。
路由类型配置
在配置文件中通过 routes
进行配置,格式为路由信息的数组。
比如:
// config.ts
export default {
routes: [
{ path: "/", component: "index" },
{ path: "/user", component: "user" },
],
};
Umi 4 默认按页拆包,从而有更快的页面加载速度,由于加载过程是异步的,所以往往你需要编写 loading.tsx
来给项目添加加载样式,提升用户体验。
属性
path
类型:string
path 只支持两种占位符配置,第一种是动态参数 :id
的形式,第二种是 *
通配符,通配符只能出现路由字符串的最后。
✅ 以下是目前支持的路由路径配置形式:
/groups
/groups/admin
/users/:id
/users/:id/messages
/files/*
/files/:id/*
❌ 以下是目前不支持的路由路径配置形式:
/users/:id?
/tweets/:id(\d+)
/files/*/cat.jpg
/files-*
component
- 类型:
string
配置 location 和 path 匹配后用于渲染的 React 组件路径。可以是绝对路径,也可以是相对路径。如果是相对路径,会从 src/pages
开始寻找。
如果指向 src
目录的文件,可以用@
,比如 component: '@/layouts/basic'
,推荐使用 @
组织路由文件位置。
routes
配置子路由,通常在需要为多个路径增加 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/>
来渲染子路由
import { Outlet } from "umi";
export default function Page() {
return (
<div style={{ padding: 20 }}>
<Outlet />
</div>
);
}
这样,访问 /list
和 /admin
就会带上 src/layouts/index
这个 layout
组件。
redirect
- 类型:
string
配置路由跳转
export default {
routes: [
{ path: "/", redirect: "/list" },
{ path: "/list", component: "list" },
],
};
访问 /
会跳转到 /list
重定向时,默认不会携带原 url 的查询参数,如需保持原参数,添加 keepQuery
选项即可:
routes: [
{ path: "/", redirect: "/list", keepQuery: true },
// 注:若你需在跳转时处理参数,可以自行实现一个跳转组件
];
wrappers
- 类型:
string[]
配置路由组件的包装组件,通过包装组件可以为当前的路由组件组合进更多的功能。 比如,可以用于路由级别的权限校验
:
- 举例如下:
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
- 高阶用法(不增加嵌套路由)
// src/hocs/withAuth.tsx
import { Navigate } from "umi";
const withAuth = (Component) => () => {
const { isLogin } = useAuth();
if (isLogin) {
return <Component />;
} else {
return <Navigate to="/login" />;
}
};
// src/pages/user.tsx
const TheOldPage = () => {
// ...
};
export default withAuth(TheOldPage);
layout
- 类型:
boolean
通过配置 layout: false
可以单独关闭某一个路由的全局布局:
// config.ts
export default {
routes: [
// 取消 login 页面的全局布局,从而自行实现整个页面
{ path: "/login", component: "@/pages/Login", layout: false },
],
};
注意
全局布局可能来自于
layouts/index.tsx
约定,或插件添加的layout(如 @umijs/max 自带的 layout 插件将自动添加菜单布局)
当配置
layout: false
时,将取消所有 layout ,此时组件内容占据整个页面,多用于登录页等场景。layout: false
仅对一级路由生效
- 有 layout 但也有自己的页面
// 自己指向自己
{
path: "/demo2",
component: "@/layouts/index",
routes: [{ path: "", component: "index" }],
},
- 有 layout 也有子路由
// 有子路由 访问地址/demo1/docs
{
path: "/demo1",
component: "@/layouts/demo1",
routes: [{ path: "/demo1/docs", component: "docs" }],
},
404 路由
- pages 下面新建 404.tsx 文件,并返回一个 React 组件,即可配置 404 路由。
[
{ path: "/", component: "@/pages/index" },
{ path: "/users", component: "@/pages/users" },
{ path: "/*", component: "@/pages/404" },
];
针不同的全局 layout
你可能需要针对不同路由输出不同的全局 layout,Umi 不支持这样的配置,但你仍可以在 src/layouts/index.tsx 中对 location.path 做区分,渲染不同的 layout 。
比如 比如想要针对 /login
输出简单布局
import { useLocation, Outlet } from "umi";
export default function () {
const location = useLocation();
if (location.pathname === "/login") {
return (
<SimpleLayout>
<Outlet />
</SimpleLayout>
);
}
// 使用 `useAppData` / `useSelectedRoutes` 可以获得更多路由信息
// const { clientRoutes } = useAppData()
// const routes = useSelectedRoutes()
return (
<>
<Header />
<Outlet />
<Footer />
</>
);
}
404 路由
约定 src/pages/404.tsx
为 404 页面,需返回 React 组件。
比如以下目录结构:
.
└── pages
├── 404.tsx
├── index.tsx
└── users.tsx
会生成路由
[
{ path: "/", component: "@/pages/index" },
{ path: "/users", component: "@/pages/users" },
{ path: "/*", component: "@/pages/404" },
];
这样,如果访问 /foo
,则 /
和 /users
都不能匹配,于是会 fallback
到 404
路由,通过 src/pages/404.tsx
进行渲染。
注意
404 只有约定式路由会自动生效,如果使用配置式路由,需要自行配置 404 的通配路由。
页面跳转
命令时路由跳转
import { history } from "umi";
// 跳转到指定路由
history.push("/list");
// 带参数跳转到指定路由
history.push("/list?a=b&c=d#anchor", state);
history.push(
{
pathname: "/list",
search: "?a=b&c=d",
hash: "anchor",
},
{
some: "state-data",
}
);
// 跳转当前路径,并刷新 state
history.push({}, state);
// 跳转到上一个路由
history.back();
history.go(-1);
组件里面可以使用
useNavigate
useNavigate 钩子函数返回一个可以控制跳转的函数;比如可以用在提交完表单后跳转到其他页面。
- 语法
declare function useNavigate(): NavigateFunction;
interface NavigateFunction {
(to: To, options?: { replace?: boolean; state?: any }): void;
(delta: number): void;
}
示例:
- 跳转路径:
import { useNavigate } from "umi";
let navigate = useNavigate();
navigate("../success", { replace: true });
- 返回上一页
import { useNavigate } from "umi";
let navigate = useNavigate();
navigate(-1);
Link 组件
然后点击 Users Page 就会跳转到 /users
地址。
- 注意
注意
- Link 只用于单页应用的内部跳转,如果是外部地址跳转请使用 a 标签
- 案例如下:
import { Link } from "umi";
export default function Page() {
return (
<div>
<Link to="/users">Users Page</Link>
</div>
);
}
路由组件参数
Umi 4 使用 react-router@6 作为路由组件,路由参数的获取使其 hooks。
match
const match = useMatch('/comp/:id')
// match
{
"params": {
"id": "paramId"
},
"pathname": "/comp/paramId/",
"pathnameBase": "/comp/paramId",
"pattern": {
"path": "/comp/:id",
"caseSensitive": false,
"end": true
}
}
location 信息
useLocation
const location = useLocation();
// location
{
"pathname": "/path/",
"search": "",
"hash": "",
"state": null,
"key": "default"
}
路由动态参数
useParams
// 路由配置 /comp/:id
// 当前 location /comp/paramId
const params = useParams();
// params
{
"id": "paramId"
}
query 信息
useSearchParams
// 当前 location /comp?a=b;
const [searchParams, setSearchParams] = useSearchParams();
searchParams.get("a"); // b
searchParams.toString(); // a=b
setSearchParams({ a: "c", d: "e" }); // location 变成 /comp?a=c&d=e