Skip to content

路由

Umi 应用是单页应用,页面地址的跳转都是在浏览器端完成的,不会重新请求服务端获取 html,html 只在应用初始化时加载一次。所有页面由不同的组件构成,页面的切换其实就是不同组件的切换,你只需要在配置中把不同的路由路径和对应的组件关联上。

路由类型配置

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

比如:

ts
// config.ts
export default {
  routes: [
    { path: "/", component: "index" },
    { path: "/user", component: "user" },
  ],
};

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

属性

path

类型:string

path 只支持两种占位符配置,第一种是动态参数 :id 的形式,第二种是 * 通配符,通配符只能出现路由字符串的最后。

✅ 以下是目前支持的路由路径配置形式:

ts
/groups
/groups/admin
/users/:id
/users/:id/messages
/files/*
/files/:id/*

❌ 以下是目前不支持的路由路径配置形式:

ts
/users/:id?
/tweets/:id(\d+)
/files/*/cat.jpg
/files-*

component

  • 类型:string

配置 location 和 path 匹配后用于渲染的 React 组件路径。可以是绝对路径,也可以是相对路径。如果是相对路径,会从 src/pages 开始寻找。

如果指向 src 目录的文件,可以用@,比如 component: '@/layouts/basic',推荐使用 @ 组织路由文件位置。

routes

配置子路由,通常在需要为多个路径增加 layout 组件时使用。

比如:

ts
export default {
  routes: [
    { path: "/login", component: "login" },
    {
      path: "/",
      component: "@/layouts/index",
      routes: [
        { path: "/list", component: "list" },
        { path: "/admin", component: "admin" },
      ],
    },
  ],
};
  • 在全局布局src/layouts/index中 通过<Outlet/>来渲染子路由
ts
import { Outlet } from "umi";

export default function Page() {
  return (
    <div style={{ padding: 20 }}>
      <Outlet />
    </div>
  );
}

这样,访问 /list/admin 就会带上 src/layouts/index 这个 layout 组件。

redirect

  • 类型:string

配置路由跳转

ts
export default {
  routes: [
    { path: "/", redirect: "/list" },
    { path: "/list", component: "list" },
  ],
};

访问 / 会跳转到 /list

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

ts
routes: [
  { path: "/", redirect: "/list", keepQuery: true },
  // 注:若你需在跳转时处理参数,可以自行实现一个跳转组件
];

wrappers

  • 类型:string[]

配置路由组件的包装组件,通过包装组件可以为当前的路由组件组合进更多的功能。 比如,可以用于路由级别的权限校验

  • 举例如下:
ts
export default {
  routes: [
    { path: "/user", component: "user", wrappers: ["@/wrappers/auth"] },
    { path: "/login", component: "login" },
  ],
};

然后在 src/wrappers/auth 中:

ts
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

  • 高阶用法(不增加嵌套路由)
ts
// 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 可以单独关闭某一个路由的全局布局:

ts
// config.ts

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

注意

  1. 全局布局可能来自于 layouts/index.tsx 约定,或插件添加的 layout(如 @umijs/max 自带的 layout 插件将自动添加菜单布局)

  2. 当配置 layout: false 时,将取消所有 layout ,此时组件内容占据整个页面,多用于登录页等场景。

  3. layout: false 仅对一级路由生效

  • 有 layout 但也有自己的页面
ts
  // 自己指向自己
    {
      path: "/demo2",
      component: "@/layouts/index",
      routes: [{ path: "", component: "index" }],
    },
  • 有 layout 也有子路由
ts
 // 有子路由 访问地址/demo1/docs
    {
      path: "/demo1",
      component: "@/layouts/demo1",
      routes: [{ path: "/demo1/docs", component: "docs" }],
    },

404 路由

  • pages 下面新建 404.tsx 文件,并返回一个 React 组件,即可配置 404 路由。
ts
[
  { path: "/", component: "@/pages/index" },
  { path: "/users", component: "@/pages/users" },
  { path: "/*", component: "@/pages/404" },
];

针不同的全局 layout

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

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

ts
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 组件。

比如以下目录结构:

bash
.
└── pages
    ├── 404.tsx
    ├── index.tsx
    └── users.tsx

会生成路由

ts
[
  { path: "/", component: "@/pages/index" },
  { path: "/users", component: "@/pages/users" },
  { path: "/*", component: "@/pages/404" },
];

这样,如果访问 /foo,则 //users 都不能匹配,于是会 fallback404 路由,通过 src/pages/404.tsx 进行渲染。

注意

404 只有约定式路由会自动生效,如果使用配置式路由,需要自行配置 404 的通配路由。

页面跳转

命令时路由跳转

ts
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 钩子函数返回一个可以控制跳转的函数;比如可以用在提交完表单后跳转到其他页面。

  • 语法
ts
declare function useNavigate(): NavigateFunction;

interface NavigateFunction {
  (to: To, options?: { replace?: boolean; state?: any }): void;
  (delta: number): void;
}

示例:

  • 跳转路径:
ts
import { useNavigate } from "umi";

let navigate = useNavigate();
navigate("../success", { replace: true });
  • 返回上一页
ts
import { useNavigate } from "umi";

let navigate = useNavigate();
navigate(-1);

然后点击 Users Page 就会跳转到 /users 地址。

  • 注意

注意

  1. Link 只用于单页应用的内部跳转,如果是外部地址跳转请使用 a 标签
  • 案例如下:
ts
import { Link } from "umi";

export default function Page() {
  return (
    <div>
      <Link to="/users">Users Page</Link>
    </div>
  );
}

路由组件参数

Umi 4 使用 react-router@6 作为路由组件,路由参数的获取使其 hooks。

match

ts
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

ts
const location  = useLocation();
// location
{
  "pathname": "/path/",
  "search": "",
  "hash": "",
  "state": null,
  "key": "default"
}

路由动态参数

useParams

ts

// 路由配置 /comp/:id
// 当前 location /comp/paramId

const params  = useParams();
// params
{
  "id": "paramId"
}

query 信息

useSearchParams

ts
// 当前 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