新版本(约定式书写)

导入包

1
2
3
4
5
6
7
8
// npm
npm install react-router-dom@6

// pnpm
pnpm add react-router-dom@6

// yarn
yarn add react-router-dom@6

在介绍 React Router 的概念以前,需要先区分两个概念:

react-router:为 React 应用提供了路由的核心功能;
react-router-dom:基于 react-router,加入了在浏览器运行环境下的一些功能。

使用

BrowserRouter

要想在 React 应用中使用 React Router,就需要在 React 项目的根文件(index.tsx)中导入 Router 组件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import { StrictMode } from "react";
import * as ReactDOMClient from "react-dom/client";
import { BrowserRouter } from "react-router-dom";

import App from "./App";

const rootElement = document.getElementById("root");
const root = ReactDOMClient.createRoot(rootElement);

root.render(
<StrictMode>
<BrowserRouter>
<App />
</BrowserRouter>
</StrictMode>
);

BrowserRouter 是最常用的路由方式,即浏览器路由。官方文档也建议将 BrowserRouter 组件用于 Web 应用程序。除了这种方式,React Router 还支持其他几种路由方式:

  • HashRouter:在路径前加入#成为一个哈希值,Hash 模式的好处是不会因为刷新页面而找不到对应路径;
  • MemoryRouter:不存储 history,路由过程保存在内存中,适用于 React Native 这种非浏览器环境;
  • NativeRouter:配合 React Native 使用,多用于移动端;
  • StaticRouter:主要用于服务端渲染时。

Link 组件和 NavLink 组件的使用方式类似,例如在产品页面有一个返回首页的按钮,需要传递给 to 需要跳转的路径:

1
2
3
4
5
6
7
8
9
10
11
12
import { Link } from "react-router-dom";
import "./styles.css";

export default function Product() {
return (
<div className="product">
<header>
<Link to="/">返回首页</Link>
</header>
</div>
);
}

如果需要对 Link 进行更多控制,也可以传递给 to 一个对象,在这个对象中,可以通过 search 属性来添加查询字符串或通过 hash 属性来传递 hash值,例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import { Link } from "react-router-dom";
import "./styles.css";

export default function Settings() {
return (
<div className="settings">
<header>
<h1>Hello World</h1>
<Link
to={{
pathname: "/settings",
search: "?sort=date",
hash: "#hash"
}}
>
设置
</Link>
</header>
</div>
);
}

Routes

下面来看看如何将路由映射为对应的页面(组件)。首先需要从 react-router-dom 中导入一个名为 Routes 的组件,它将包含可以在页面特定部分显示的所有不同的路由。

在 index.tsx 中进行如下修改:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import { NavLink, Routes, Route } from "react-router-dom";
import Product from "./Product";
import "./styles.css";

export default function App() {
return (
<div className="App">
<header>
<h1>Hello World</h1>
</header>
<nav>
<NavLink to="">首页</NavLink>
<NavLink to="product">产品</NavLink>
<NavLink to="about">关于</NavLink>
</nav>
<Routes>

</Routes>
</div>
);
}

我们需要在 Routes 组件中使用 Route 组件来定义所有路由。该组件接受两个 props:

path:页面 URL 应导航到的路径,类似于 NavLink 组件的 to;
element:页面导航到该路由时加载的元素。

Route 组件用于将应用的位置映射到不同的 React 组件。例如,当用户导航到 /product 路径时呈现 Product 组件,可以这样来写:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import { NavLink, Routes, Route } from "react-router-dom";
import Product from "./Product";
import About from "./About";
import Home from "./Home";
import Error from "./Error";
import "./styles.css";

export default function App() {
return (
<div className="App">
<header>
<h1>Hello World</h1>
</header>
<nav>
<NavLink to="">首页</NavLink>
<NavLink to="product">产品</NavLink>
<NavLink to="about">关于</NavLink>
</nav>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/product" element={<Product />} />
<Route path="/about" element={<About />} />
<Route path="*" element={<Error />} />
</Routes>
</div>
);
}

如果想要在所有 Route 都不匹配时就渲染 404 页面,只需将 404 页面对应的 Route 的 path 设置为 *:

1
2
3
4
5
6
<Routes>
<Route path="/" element={<Home />} />
<Route path="product" element={<Product />} />
<Route path="about" element={<About />} />
<Route path="*" element={<Error />} />
</Routes>

编程式导航

React Router 提供了两种不同的编程式导航方式:

声明式导航组件:<Navigate> 组件
命令式导航方法:useNavigate Hook

我们可以使用这两种编程的方式来跳转到指定的路由,也可以实现路由的重定向,比如在浏览器的地址栏输入一个 URL 并进行跳转时,如果应用中没有定义该路由,就跳转到应用的首页。

<Navigate>组件是一种声明式的导航方式。使用 Navigate 组件时,首先需要从 react-router-dom 导入 Navigate 组件。然后在 Navigate 组件中通过 to props 来指定要跳转的路径:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import { NavLink, Routes, Route, Navigate } from "react-router-dom";
import Product from "./Product";
import About from "./About";
import Home from "./Home";
import "./styles.css";

export default function App() {
return (
<div className="App">
<header>
<h1>Hello World</h1>
</header>
<nav>
<NavLink to="">首页</NavLink>
<NavLink to="product">产品</NavLink>
<NavLink to="about">关于</NavLink>
</nav>
<Routes>
<Route path="/" element={<Home />} />
<Route path="product" element={<Product />} />
<Route path="about" element={<About />} />
<Route path="*" element={<Navigate to="/" />} />
</Routes>
</div>
);
}

这样,当在浏览器地址栏输入一个未定义的路由时,就会要转到首页。

useNavigate

useNavigate Hook是一种命令式导航方式。使用这个 Hook 时,首先需要从 react-router-dom 中导入 useNavigate,然后传递给它需要跳转的路由即可。假如在提交完表单之后,跳转到主页,可以这样实现:

1
2
3
4
5
6
7
8
9
10
11
import { useNavigate } from 'react-router-dom'

function Register () {
const navigate = useNavigate()

return (
<div>
<Form afterSubmit={() => navigate('/')} />
</div>
)
}

通过路由传递状态

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//发送数据
<Link to="/" state={"From Product"}>

//接受数据
import { useLocation } from "react-router-dom";
import "./styles.css";
export default function Settings() {
let location = useLocation();
return (
<div className="App">
<header>首页</header>
<p>{location.state}</p>
</div>
);
}

引用自React Router v6 完全指南

旧版本(类似vue一样的格式书写)

最新官方网站
暂不支持中文,阅读起来稍显麻烦.
该篇教程面向的是react router6.22.3的版本,并且使用的是ts

因为我也不是很懂,所以在看完一小部分后,准备写一个面向新手的教程(主要是怕自己以后忘了怎么写路由)

创建项目

1
2
3
4
5
6
7
# xxx是你的项目名称
npx create-react-app xxx -template typescript

# 安装react-router-dom
npm install react-router-dom@6.22.3
# types暂时没有支持这么高的版本
# npm instasll @types/react-router-dom@6.22.3

index.tsx就是我们项目的入口地址:
react-router速成-2024-03-29-12-42-31

我们进行以下的修改:

  1. 简化App.tsx的内容

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    import React from 'react';

    function App() {
    return (
    <div >
    Hello react
    </div>
    );
    }

    export default App;
  2. 修改index.tsx为这部分内容

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    import React from 'react';
    import ReactDOM from 'react-dom/client';
    import './index.css';
    import App from './App';
    import reportWebVitals from './reportWebVitals';
    import {
    createBrowserRouter,
    RouterProvider,
    Route,
    Link,
    } from "react-router-dom";

    //添加路由
    const router = createBrowserRouter([
    {
    //路径
    path: "/",
    //元素(可以导入其他的元素,也可以直接编写一个html)
    element: <App />,
    },
    {
    path: "/about",
    element: <div>About</div>,
    },
    {
    path: "/contact",
    element: <div>Contact</div>,
    },
    ])

    const root = ReactDOM.createRoot(
    document.getElementById('root') as HTMLElement
    );
    root.render(
    <React.StrictMode>
    //绑定router
    <RouterProvider router={router}/>
    </React.StrictMode>
    );

    // If you want to start measuring performance in your app, pass a function
    // to log results (for example: reportWebVitals(console.log))
    // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
    reportWebVitals();

以上就是最基础的路由配置,你已经入门了.

子路由(Index)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import {Outlet} from 'react-router-dom'
...
{
path: "/about",
element: <div>About
<Outlet/>
</div>,
children: [
{index: true, element: <div>Index</div>},
{
path: "me",
element: <div>About me</div>,
}
]
},

子页面会展示在Outlet内.
子路由可以设定一个index元素,可以设置默认展示的子页面

配置错误页面

如这样配置,或者添加一个react元素,每次访问到没有的页面都会进入这个内容

1
2
3
4
5
{
path: "/",
element: <App/>,
errorElement: <div>404</div>,
},

动态路由,参数传递(loader)

不使用loader,直接通过useParams来获取参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
const router = createBrowserRouter([
{
// it renders this element
element: <Team />,

// when the URL matches this segment
path: "teams/:teamId",

// with this data loaded before rendering
loader: async ({ request, params }) => {
return fetch(
`/fake/api/teams/${params.teamId}.json`,
{ signal: request.signal }
);
},

// performing this mutation when data is submitted to it
action: async ({ request }) => {
return updateFakeTeam(await request.formData());
},

// and renders this element in case something went wrong
errorElement: <ErrorBoundary />,
},
]);

//useParams
function Team() {
let params = useParams();
console.log(params.teamId); // "hotspur"
}

我们通过类似"teams/:teamId"的形式设置动态路由,如果要获取动态路由传递过来的参数,通常是使用useParams()的方法来获取

使用loader

我们使用loader,就不需要使用useParams(),他会自动替你执行useParams的方法,然后你只需解构就能获取参数,然后通过修改数据来传入更多的内容.
图示:
react-router速成-2024-03-29-13-12-39
如果我们是使用vue的话,可能会获取参数后,在mount方法中,设置我们需要初始化的所有数据,而react帮我们把数据初始化的过程抽出来放在loader中

1
2
3
4
5
6
7
//类似这样
export default function Root() {
const { contacts } = useLoaderData();
return (
<div>contacts.data</div>
)
}

action

这个功能可能用的不多,他会把你页面中原生的html的form表单全部捕获,原本发送到服务器的表单数据,会发送到action中,然后在进行细致的操作
我这里不详细讲,因为可能用的不多,想更多了解到可以看看官网