ReactRouter-6.x的使用

384 阅读4分钟

React Router V6介绍

React Router在2021年12月发布了 V6 稳定版本。与 V5 版本相比,V6 版本移除了 V5 版本中的一些 API,如 useHistorySwitchRedirect 这三个常用的 API

而在一些 API 的用法也发生了改变,如 Routecomponent 属性改成了 element

// V5 版本
import Home from "./Home"
<Route path="/" component={<Home />} />

const Home = React.lazy(() => import("./Home"));
<Route path="/" component={Home} />

在 V5 版本中,以上这两种写法都是允许的

// V6 版本
import Home from "./Home"
<Route path="/" element={<Home />} />

const Home = React.lazy(() => import("./Home"));
<Route path="/" element={<Home />} />

在 V6 版本中,写法则变成以上这方式

实际上手

创建一个 React 项目

$ yarn create react-app router-demo --template tyepscript

这里创建的是一个 React + TypeScript 的项目

另外为了方便界面的好看,这里直接使用了 antd

$ yarn add antd

antd 的如何使用这里不多说明,详细使用说明参考:ant.design/docs/react/…

项目目录结构

我这里已经删除了一些并不是特别重要的文件

由于在实际的项目中,嵌套路由是最常使用的,因此这里直接使用嵌套路由

嵌套路由实现方式一

src/index.tsx

import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import { BrowserRouter, Routes, Route } from "react-router-dom"
import Login from "./pages/login";
import PageLayout from './layout';

function Index() {
    return (
        <BrowserRouter>
            <Routes>
                <Route path='/login' element={<Login />} />

                <Route path='/*' element={<PageLayout />} />

            </Routes>
        </BrowserRouter>
    )
}

const root = ReactDOM.createRoot(
    document.getElementById('root') as HTMLElement
);
root.render(
    <Index />
);

src/routes.ts

import { lazy } from "react";

export interface Route {
    key: string;
    name: string;
    component?: any;
}

export const routes: Route[] = [
    {
        key: "home",
        name: "首页",
        component: lazy(() => import("./pages/home")),
    },
    {
        key: "about",
        name: "关于",
        component: lazy(() => import("./pages/about")),
    },
]

src/layout/index.tsx

import React from "react";
import { routes } from "../routes";
import { Link, Outlet, Route, Navigate, Routes } from "react-router-dom"
import { Space } from "antd";

function PageLayout() {

    return (
        <div>
            <Space>
                {routes.map(item => (
                    <Link to={item.key} key={item.key}>{item.name}</Link>
                ))}
            </Space>

            <main>
                <Routes>

                    {routes.map(item => (
                        <Route path={item.key} element={
                            <React.Suspense fallback={<>路由加载中...</>}>
                                <item.component />
                            </React.Suspense>
                        } key={item.key} />
                    ))}
                    <Route index element={<Navigate to={"/home"} />} />
                </Routes>
            </main>
        </div>
    )
}

export default PageLayout;

以上这个用法是根据 V5 版本进行修改过来的,如 src/layout/index.tsx 文件,只是将 Switch 修改成 Routes ,将 component 修改成 element ,另外再增加一个 React.Suspense 组件

React.Suspense 组件:是在 React 16 版本之后引入的,其作用是为了对组件进行懒加载,当需要用到组件时才加载,搭配 React.lazy 使用。fallback 可以理解成加载过程中的一个动作,放置加载动画

嵌套路由实现方式二

这里只需要对 src/layout/index.tsxsrc/index.tsx 进行简单的修改即可

src/index.tsx

删除掉 <Route path='/*' element={<PageLayout />} />

增加以下内容即可

<Route path="/" element={<PageLayout />}>
    {routes.map(item => (
        <Route path={item.key} element={
            <React.Suspense fallback={<>路由加载中...</>}>
                <item.component />
            </React.Suspense>
        } key={item.key} />
    ))}
    <Route index element={<Navigate to={"/home"} />} />
</Route>

src/layout/index.tsx

直接将 <main> </main> 的内容清空

import {Outlet} from "react-router-dom"

<main></main> 中写入 <Outlet /> 即可

关于 Outlet 组件,官网是这么介绍的

An <Outlet> should be used in parent route elements to render their child route elements. This allows nested UI to show up when child routes are rendered. If the parent route matched exactly, it will render a child index route or nothing if there is no index route.

大致的意思就是说,Outlet 会渲染出子路由中的元素内容,若没有则不进行渲染

路由跳转

在开头说了 React-Router V6 版本移除了 useHistroy,这是不是意味着不能采用函数式跳转路由了?

移除掉相对应的 API ,通用的也会存在与之替代的 API

React-Router V6 版本新增 use 相关的 API:useHrefuseInRouterContextuseLinkClickHandleruseLinkPressHandleruseLocationuseMatchuseNavigateuseNavigationTypeuseOutletuseOutletContextuseParamsuseResolvedPathuseRoutesuseSearchParamsuseSearchParams (React Native)

这里挑选几个开发中常用的来简单说明一下

useLocation

官方对 useLocation 的定义:

declare function useLocation(): Location;

interface Location extends Path {
  state: unknown;
  key: Key;
}

有时候我们需要获取在当前所处的路由访问路径,在 V5 版本中会使用到 useHistory

import {useEffect} from "React"
import {useHistory} from "react-router-dom"

function Home() {
    
    const history = useHistory();
    
    useEffect(() => {
        const pathname = history.location.pathname;
    }, []);
    
    return (
        <div></div>
    )
}

而 V6 版本则直接提供了 useLocation 方便获取到路由的访问路由

import {useEffect} from "React"
import {useLocation} from "react-router-dom"

function Home() {
    
    const location = useLocation();
    
    useEffect(() => {
        const pathname = location.pathname;
    }, []);
    
    return (
        <div></div>
    )
}

useLocation 返回的参数如下:

useNavigate

路由跳转是不可或缺的,除了我们常使用的 Link 进行路由跳转,但实际开发中,有很多场景下需要使用函数跳转的,很显然 Link 并不能够很好满足需求

官方对 useNavigate 的定义:

declare function useNavigate(): NavigateFunction;

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

在 V5 版本中,可以通过获取 const history = useHistory() ,然后通过 history.push() 或者是 history.replace({pathname: ""}) 进行路由跳转

而在 V6 版本中,则可以用过 const navigate = useNavigate() 来实现路由的函数式跳转

const navigate = useNavigate();

navigate(路由地址) 

navigate(路由地址, {replace: true | false})

useParams

官方对 useParams 的定义:

declare function useParams<
  K extends string = string
>(): Readonly<Params<K>>;

使用:

const params = useParams();

useSearchParams

官方定义:

declare function useSearchParams(
  defaultInit?: URLSearchParamsInit
): [URLSearchParams, SetURLSearchParams];

type ParamKeyValuePair = [string, string];

type URLSearchParamsInit =
  | string
  | ParamKeyValuePair[]
  | Record<string, string | string[]>
  | URLSearchParams;

type SetURLSearchParams = (
  nextInit?: URLSearchParamsInit,
  navigateOpts?: : { replace?: boolean; state?: any }
) => void;

useRoutes

官方定义

declare function useRoutes(
  routes: RouteObject[],
  location?: Partial<Location> | string;
): React.ReactElement | null;

这个 API 有点类似于 Vue-Router 定义路由的方式,目的是在 .ts.js 文件中定义路由

用法

import {useRoutes} from "react-router-dom";

function GetRoutes() {
    return useRoutes([
        {
            path: "/",
            element: <Home />,
            children: [
                { path: "workplace", element: lazy(() => import("./workplace")) }
            ]
        }
    ]);

}

function App() {
    return (
        <div>
            <GetRoutes />
        </div>
    )
}

关于其他的 API 这里就不多做讲解了

最后

React-Router V6 版本相对于 v5 版本有着很大的改变,倘若项目中使用的是 v5 版本,这里不太建议升级为 v6 版本,升级过后就会发现有着大量的 API 替换工作

文章可能会存在一些问题,如若有错误,请在评论区说明,作者会及时进行修改