本文编写自26届同学:狗儿要听狗儿歌
React Router
官方文档是英文的,所以一些部分我就没做翻译,给你们原汁原味的体验
Setup
还是像上次学习react时那样,先简单创建一个react项目,这里不再做演示
清除一些没用的css及组件文件后
打开编译器的终端
注意终端的路径是否为项目的路径(一般是)
输入并执行 npm install react-router-dom localforage match-sorter sort-by
安装没有报错后,即可开始正式的学习
Adding a Router
react router 的出现是为了解决一个项目要写多个html的问题,有了router之后,我们的项目就只需要一个index.html 就可以实现多个页面的效果
First thing to do is create a Browser Router and configure our first route. This will enable client side routing for our web app.
The main.tsx
file is the entry point. Open it up and we’ll put React Router on the page.
import * as React from "react";
import * as ReactDOM from "react-dom/client";
import {
createBrowserRouter,
RouterProvider,
} from "react-router-dom";
const router = createBrowserRouter([
{
path: "/",
element: <div>Hello world!</div>,
},
]);
ReactDOM.createRoot(document.getElementById("root")!).render(
<React.StrictMode>
<RouterProvider router={router} />
</React.StrictMode>
);
有了这样一个,router 对象,相当于就是配置了一个路由,现在运行项目来看看效果。
似乎和原来没什么区别,但是在这里我们仅仅只配置一个根路由,这个router对象理解为在写一个配置项,现在这个router 对象的意思是,在路径为/ <div>Hello world!</div>
。
当我们打开项目时,默认是空路径,当我们将路径切换为/index 时就会出现报错
Handling Not Found Errors
我们来写一个error-page组件让它在路径出现错误时,展示出来
import { useRouteError } from "react-router-dom";
interface Error {
statusText: string,
message: string
}
export default function ErrorPage() {
const error = useRouteError() as Error;
console.error(error);
return (
<div id="error-page">
<h1>Oops!</h1>
<p>Sorry, an unexpected error has occurred.</p>
<p>
<i>{error.statusText || error.message}</i>
</p>
</div>
);
}
将router改为
const router = createBrowserRouter([
{
path: "/",
element: <div>Hello world!</div>,
errorElement: <ErrorPage />,
}
]);
现在可以发现报错时展示的就是我们想要的页面
The Root Route
我们先写一个Root 组件,让它来作为我们组件的根基(一般我们可以用App组件来作为根组件)
并且我们将 router 分离到其他文件方便管理
这是现在页面的效果,我们希望点击上的的不同数字时,下放的粉色可以展示不同的组件
这就可以用到 router 来实现这个效果
首先我们需要配置 router,因为下方粉色区域组件切换时,Root 并不会修改,而且从包容关系上说,下面组件的切换只是子组件的切换,所以我们将配置/ 的子路由
import ErrorPage from "../page/error-page";
import {createBrowserRouter} from "react-router-dom";
import One from "../page/root/one"
import Two from "../page/root/two"
import Three from "../page/root/three"
import Four from "../page/root/four"
import Root from "../page/root";
export const router = createBrowserRouter([
{
path: "/",
element: <Root />,
errorElement: <ErrorPage />,
children: [
{
path: "one",
element: <One></One>
},
{
path: "two",
element: <Two></Two>
},
{
path: "three",
element: <Three></Three>
},
{
path: "four",
element: <Four></Four>
}
]
}
]);
这里我们为/ 配置了四个子路由,我们希望在/one 展示 组件 One,同理在/two 展示 Two……
在 Root 组件中 ,我们更改代码为
import "./index.scss"
import {Link, Outlet} from "react-router-dom";
export default function App(){
const numberList = ["one", "two", "three", "four"]
return (
<>
<div className='container'>
<ul className='header'>
{numberList.map((number, index) => {
return <Link key={index} to={`/${number}`}>{index}</Link>
})}
</ul>
<div className='body'>
<Outlet></Outlet>
</div>
</div>
</>
);
}
这里的 Link 组件最终会被解析为<a>
标签,所以点击这个 Link ,就可以达到变化路径的作用,而下面的 Outlet 将起到占位的作用,其作用在于告诉react-router ,我们的组件 One 会显示在什么地方
另外我们需要常见四个组件用来对应我们的路由
最后效果如下
这样我们就利用路由实现了我们想要的效果
URL Params
我们注意到因为我们 One、Two、Three、Four 这四个组件之前很相似,我们好像没有必要写四个组件
能不能只写一个组件,然后通过类似 props 的方式,传入参数来展示不同的效果
我们将router 改为
export const router = createBrowserRouter([
{
path: "/",
element: <Root />,
errorElement: <ErrorPage />,
children: [
{
path: "index/:number",
element: <Number></Number>
}
]
}
]);
这里 index/:number 表示我们还需要传送一个number 参数
同时需要创建一个Number 组件
import {useParams} from "react-router-dom";
export default function App() {
const {number} = useParams()
return <>
{number}
</>
}
这个number组件用useParams 这个Hook函数来获取url 中的参数,同时又放在组件中渲染
将Root 组件改为
export default function App(){
const numberList = ["one", "two", "three", "four"]
const navigate = useNavigate();
const handleClick = function (_: React.MouseEvent<HTMLLIElement>, number: string) {
navigate("/index/" + number)
}
return (
<>
<div className='container'>
<ul className='header'>
{numberList.map((number, index) => {
return <li key={index} onClick={(e) => {
handleClick(e,number)
}}>{index}</li>
})}
</ul>
<div className='body'>
<Outlet></Outlet>
</div>
</div>
</>
);
}
这里我们采用另外一种方式,useNavigate 这个Hook 函数 来进行跳转
这种方式在一些时候比较方便
在上面的效果中,我们一直使用 createBrowserRouter
来创建一个router,但是用这个函数创建的 router 有一个缺陷,就是在这个项目上线的时候(现在只是前端的run dev
还看不出问题),但我们在 /index/one
时点击刷新按钮,可能会出现404 报错, 原因在于浏览器错误的把我们的路由部分也当作url 拿去访问页面,即 浏览器回去 http://localhost:5173/index/one 寻找html
返回,然而,后端可能并未对这个链接做重定向,然而这个接口在后端是没有返回值的,所以我们会得到404报错,因此这样的写法,还得麻烦后端做重定向
所以我们还有另一种方式来创建router
export const router = createHashRouter([
{
path: "/",
element: <Root />,
errorElement: <ErrorPage />,
children: [
{
path: "index/:number",
element: <Number></Number>
}
]
}
]);
通过 createHashRouter
来创建的router,在域与路由路径之间多了一个#,我们把#后面的部分叫做 hash
这样 hash 就不会被浏览器发送到后端,从而导致 404
当然前者主要美观,后者因为多了个#,有人觉得不美观, 显然 hashRouter 更加方便
History Stack
For example, consider the user:
- clicks a link to
/dashboard
- clicks a link to
/accounts
- clicks a link to
/customers/123
- clicks the back button
- clicks a link to
/dashboard
The history stack will change as follows where bold entries denote the current URL:
/dashboard
/dashboard
,/accounts
/dashboard
,/accounts
,/customers/123
/dashboard
,/accounts
,/customers/123
/dashboard
,/accounts
,/dashboard
“stack”一词在计算机科学中通常用来指代一种数据结构,即栈。栈是一种具有特定行为的数据集合,其特点是具有后进先出(Last-In, First-Out,LIFO)的操作顺序。
可以将栈想象成一个类似于一叠盘子的结构,你可以在这叠盘子的顶部放置或移除元素,但是只能操作最顶部的元素,而不能操作底部的元素,除非你先移除掉位于顶部的元素。这种特性使得栈在很多应用中都非常有用,比如函数调用的管理、表达式求值、浏览器历史记录等。
这里也是一样,你可以点击你写的页面,来理解这个概念
注意上方 hash 的变化循序
这两个按钮的功能也可以用函数来实现,在上文我们所提到的 useNavigate
就实现
Loader
URL segments, layouts, and data are more often than not coupled (tripled?) together.
Because of this natural coupling, React Router has data conventions to get data into your route components easily.
There are two APIs we’ll be using to load data, loader
and useLoaderData
. First we’ll create and export a loader function in the root module, then we’ll hook it up to the route. Finally, we’ll access and render the data.
我们先创建一个接口函数
router 配置为
export const router = createHashRouter([
{
path: "/",
element: <Root />,
errorElement: <ErrorPage />,
loader: rootLoader,
children: [
{
path: "index/:number",
element: <Number></Number>
}
]
}
]);
这种方式能够将数据注入到路由中,在组件渲染之前
不过它仅仅只能在上面的这些createRouter情况下使用
作业: 自己练练吧
原文链接:https://juejin.cn/post/7351685099005640755 作者:LanShanFE