开源地址:GitHub - remix-run/react-router: Declarative routing for React
react 路由有三种写法:传统组件法、useRoutes 法、data Router 法
- 官方推荐的写法是 data Router
- 这里用 data Router
- route 的 handle 类似 vue-router 中的 meta
- handle 属性仅在 data Router 下有效
- 获取 handle 的 useMatches 只能在 data Router 下调用
// routes.tsx
import { createBrowserRouter, Navigate } from "react-router-dom";
import { PageIndex } from "@/page";
export const router = createBrowserRouter([
{
// 确保所有的路由都走 AuthRoute 组件
path: "/",
// 这个组件做鉴权和处理标题
element: <AuthRoute />,
// 真正的网页
children: [
{ path: "", element: <PageIndex />, handle: { title: "首页" } },
{ path: "login", element: <PageLogin />, handle: { title: "登录页" } },
// 没有匹配到的路由重定向到 404
{ path: "*", element: <Navigate to="404" replace /> },
{ path: "404", element: <Page404 />, handle: { title: "404页" } },
],
},
]);
// App.jsx
import { RouterPrivider } from "react-router-dom";
import { router } from "./routes";
export function App() {
return <RouterProvider router={router} />;
}
鉴权 & 网页标题
- 所有路由变化都会触发 AuthRoute 的更新
- AuthRoute 返回的 JSX 就是路由的结果
// route/index.ts
/**
* 实现路由鉴权,并根据路由修改网页标题
* @returns 通过时为 page,反之 PageLogin
*/
function AuthRoute() {
/**
* 1.获取路由匹配信息
* 2.获取子路由元素
* 3.获取 store 中的登录信息
*/
const matches = useMatches(); //这个钩子只有 data Router 下能用
const outlet = useOutlet();
const isLogined = useAppSelector((state) => state.auth.isLogined);
/**
* 当前路径是否在白名单?
* 当前是否已登录?
* 都不是则重向定到 PageLogin
*/
const page = useMemo(() => {
const { pathname } = matches[1];
const isInWL = whiteList.includes(pathname);
if (isInWL) return outlet;
if (isLogined) return outlet;
return <Navigate to="login" replace />;
}, [isLogined, matches, outlet]);
/**
* 模仿 vue-router 后置钩子修改网页标题
*/
useEffect(() => {
const title = (matches[1].handle as any)?.title;
const isHasTitle = typeof title === "string";
if (isHasTitle) {
document.title = title;
}
}, [matches]);
return page;
}
懒加载
- vue-router 里的 component 可以直接写成一个回调实现懒加载
- 但是 react-router 没有,需要我们自己写 hook
// useLazy.tsx
import { Skeleton } from "antd";
import React from "react";
/**
* @function useResize 使用的类型
*/
export namespace Type {
export type defRC = {
default: React.ComponentType<any>;
};
}
/**
* 类似 React.lazy,实现组件懒加载
* @param callback 返回 Promise 的函数
* @returns JSX
*/
export function useLazy(callback: () => Promise<Type.defRC>) {
const LazyComp = React.lazy(callback);
return (
<React.Suspense fallback={<Skeleton active />}>
<LazyComp />
</React.Suspense>
);
}
用上 hook 后,route 的写法:
// routes.tsx
import { Navigate, RouteObject } from "react-router-dom";
export const routes: RouteObject[] = [
{
path: "/home",
element: useLazy(() => import("@/page/home")),
},
];