想想我们访问一个应用或网页,往往都不可能总是一个界面上放置所有内容吧。所以我们往往会体验到页面直接各种跳转,应用中各种状态界面的切换。于是我们把这些过程中标志页面或状态的名称叫做路由。
目录
内容
我们为什么需要路由
我们一个简单的helloworld应用, 打开就只需要让人看到hello world即可;
但假设我们需要一个进去见面hello world, 点一下“打招呼”的按钮后,就显示"你好";再点击一下“再见”按钮后,就显示"再见"。这种场景下,我们实际上可以理解为3个层次:见面,你好打招呼,再见。这是多个阶段,有状态变化的过程。人们在不同阶段会说不同的话。于是我们需要“路由”来做这个状态管理。
react-router的来龙去脉
通常路由是浏览器的行为,是与后台有关的内容。每个后端路由会为访问的内容分配一个路径地址,以此来标记该状态。
但在前端,我们也有这样的需要,但我们是在前端页面中啊,假设一个不需要后端支持的内容,难道还不配拥有路由机制了吗?显然是不合理的。于是有人就利用浏览器的机制来实现这种功能。我们先说说react-router有哪些路由模式,以及各自的特点。
第一种是hashRouter
这种路由器使用 URL 的哈希部分(# 之后的部分)来表示应用程序的路由状态。当用户在浏览器中导航时,哈希部分的更改不会导致服务器请求,因此这种路由器非常适合于静态文件服务器或者无法配置服务器以处理客户端路由的环境。然而,哈希 URL 对于 SEO 不太友好,因此在需要 SEO 的应用程序中不建议使用。
第二种是browserRouter
这种路由器使用 HTML5 历史 API(pushState、replaceState 和 popstate 事件)来保持 UI 和 URL 的同步。它为应用程序提供了干净的 URL,有利于 SEO。但是,为了使 BrowserRouter 正常工作,服务器需要配置为始终返回单页应用程序的入口 HTML 文件,以便在用户刷新页面或直接访问某个路由时,应用程序仍然可以正常运行。
第三种是memoryRouter
这种路由器将路由历史记录保存在内存中,而不是在 URL 中。这意味着当用户在应用程序中导航时,URL 不会发生变化。MemoryRouter 主要用于非浏览器环境,如 React Native 或者服务器端渲染,以及在单元测试中模拟路由行为。
实践
我们以clock为例来实践一个吧(我们以browserRouter为例)。由于clock通常会包含四个大模块(时钟,闹钟,倒计时,秒表), 然后每个模块又各自有自己的多个状态。所以正好能够很好的说明路由的使用场景。
怎么开始呢? 我一直想如何能用最通俗的方式介绍react-router。我想还是应该从最根本开始说。
回忆下我们最简单的react的hello world是如何开始的: index.js包裹了app.js; 然后app.js里面层层包裹各种组件的代码。没错,这就是没有路由参与的情况: 只有一个显示状态,这个状态下所有的组件都显示在内。
回到我们我们接下来要实现的内容,是否可以将他简单的理解为每个路由地址对应一个hello world应用? 当然是可以的。既然这样,我们要做的就是可能大概应该会是如下:
//react 应用
//路由
//路由1 => 实现其中对应的内容,会包含各自组件内容
//路由1.1 => 实现其中对应的内容,会包含各自组件内容
//路由1.2 => 实现其中对应的内容,会包含各自组件内容
//路由2 => 实现其中对应的内容,会包含各自组件内容
//路由2.1 => 实现其中对应的内容,会包含各自组件内容
//路由3 => 实现其中对应的内容,会包含各自组件内容
这样想想,设计实现上肯定是可行的。于是我们带着疑问去看下react-router(我以最新的v6版本为例)。
确实官网进入后就能看到一个示例:
import * as React from "react";
import * as ReactDOM from "react-dom/client";
import {
createBrowserRouter,
RouterProvider,
} from "react-router-dom";
import "./index.css";
const router = createBrowserRouter([
{
path: "/",
element: <div>Hello world!</div>,
},
]);
ReactDOM.createRoot(document.getElementById("root")).render(
<React.StrictMode>
<RouterProvider router={router} />
</React.StrictMode>
);
这个示例里面,很明显,它就做了两件事:
- const router = createBrowserRouter(...)
-
<React.StrictMode> <RouterProvider router={router} /> </React.StrictMode>
看来,我们一开始的假设是完全正确的。那么接下来我们深入看一下,react-router里面是如何一一深入实现其中的细节的呢?
在我的Clock实例当中,我就先把上述的骨架用进去。
可以看到,核心就是RouterData是什么?它其实就是如何定义路由。这其实就是react-router为我们定制的语法了。学语言嘛,说白了不就是熟悉语言规则后,按照他们定制的规则再写出来嘛。
看看routerData吧。
我把这个内容翻译一下吧。先只看核心的。
- import了一些组件
- RouterData就是路由规则列表,
- path 是路由地址
- element 是对应的组件
- children 是当前路由的子路由规则
- index 就是表示默认路由,即 / 就会显示
这个组件的内容了。
- index 就是表示默认路由,即 / 就会显示
如此嵌套与反复。就是这样。
但有一点可能需要注意一下,这些路由似乎都是要变的时候,就整个页面都变化了。但我们实际使用时,肯定会有只有页面中某块内容随着路由变化而变化吧。所以这个时候就需要用到outlet。
outlet怎么使用的? 看下我的代码!
看核心!:
- 引入Outlet
- Outlet就是这里面涉及变化的出口。换句话说就是接下来变化的内容,都只会在Outlet这里填充。那么其他的地方就不会变化了。
到这一步,看来一切很很简单。我们从头梳理一下,加深理解:
- 如果只有单一状态,不需要路由加入,则我们只需要
, App再逐层包裹里面的内容组件即可; - 如果涉及到多个状态,我们则需要react-router。怎么开始用它呢?
- 创建browwerRouter实例:
const router = createBrowserRouter(routerData)
- react中进入
<React.StrictMode> <RouterProvider router={router} /> </React.StrictMode>
- 定义路由
const RouterData = [ { path: "/", element: <App/>, children:[{ path: "/a", element: <App/> }, ...]}, { path: "/city", element: <City/> } { path: "/province", element: <Province/> } ]
- 如果需要只变化某一块就引入Outlet来填充到对应为止即可!
- 创建browwerRouter实例:
嗯,就是这样,我们理解内容,就抓住它的重点和精髓即可。细致末节可以后续去丰富。
那么我们接下来就能愉快地逐步逐步去实现这个clock了。
0 条评论