这是我参与更文挑战的第1天,活动详情查看: 更文挑战
「前言」
react-router 库其实也没什么好写,毕竟只要有一点 React 基础肯定就知道或者使用过 react-router。今天阅读 react-router 源码的时候突然想到一些好玩的东西。
这篇文章我记录我自己的心路历程,从根源上理解 react-router。
「做个类比」
react 组件化的思想让开发者开发项目时就像玩「换装游戏」一样,把多个组件拼接成一个项目。有些情况,我们只是在某个位置替换不同的组件,而不需要整个页面都重新加载。
利用 换装游戏 说明,人物角色的身体是固定好的,根据不同的设置,个性化的角色的帽子,发型,衣服,武器会会不一样。也就是在同一个位置,展示出了不同的内容。
类比到 web 系统上,比如常见的 CMS 系统布局。头部+左侧菜单栏都是固定的,就像换装游戏的角色身体;变化的仅仅是右下内容区域,也就是发型,武器等,当前如果系统复杂一点,可以变化的部分就很多了。
一种实现方式可以通过一种 state 状态来控制右下内容区域的需要渲染的内容组件,但是这种方式只能在当前 session 下可控,只要刷新了页面,之前的 state 状态都会丢掉。显然对于有的一些项目来说这是不合理的。
浏览器的 URL 内容是一个很好的记录页面状态的信息。SPA 形式之前,不同的 URL 都会向服务器发送请求,然后服务器返回一个完整的 HTML 页面。 SPA 形式下,URL 路由的变化只会在 浏览器端处理,所以组装页面的任务就交给了浏览器。
此时 react-router 出现了,通过使用 react-router 以及阅读 react-router 源码,发现 react-router 不过就是基于 URL 内容变化的「智能拼图工具」。
「源码分析」
react-router 源码提供了 路由必需的基础组件,对于 web 应用还需要适配 react-router-dom,源码结构如下图。其中包含了包工程结构以及右边 react-router 导出的组件列表。
「简单例子」
import {
BrowserRouter,
Route,
Switch,
} from "react-router-dom";
ReactDOM.render(
<BrowserRouter>
<Switch>
<Route path='/' exact={true} component={App1} />
<Route path='/app2' component={App1} />
<Route path='/app2' component={App2} />
<Route path='/app3' component={App3} />
</Switch>
</BrowserRouter>,
document.getElementById("root")
);
通过 简单例子 代码可以看出来 react-router 组件使用结构。<Route> 组件中包含需要渲染的业务组件,path属性决定什么情况渲染业务组件 。
最外层需要包裹一层 <BrowserRouter> 组件,从源码可以看出 Router 中嵌套了两层 React 的 Context:RouterContext 和 HistoryContext。
RouterContext 给子组件消费者提供了 「history」「location」「match」「staticContext」四个属性。
HistoryContext 提供给子组件消费者提供了「history」属性。
Route 组件的作用是从 Router 组件的上下文中解构出属性,并同时组装自己props属性,然后新建一个 RouterContext 上下文并将props传递给业务组件。
通过简单例子可以看出,从项目的入口列出 Route 列表,就是把整个页面当作一个拼图,然后通过路由替换整个页面。具体的业务组件中需要替换某一块区域,那么就在需要替换的地方列出 Route 列表。
「嵌套例子」
// src/App1.jsx
import React, { useState } from "react";
import { Layout, Menu } from "antd";
import { Route, Switch, Link } from "react-router-dom";
const { Header, Content, Sider, Footer } = Layout;
export default function App1({ routes = [] }) {
const [selecgtKey, setSelectKey] = useState([window.location.pathname]);
return (
...
<Content>
//核心代码
<Switch>
{routes.map((route, inx) => {
return (
<Route
key={inx}
path={route.path}
exact={route.exact}
render={() => <route.component routes={route.routes} />}
/>
);
})}
</Switch>
</Content>
...
);
}
嵌套代码只替换效果图中红框内容,只需要在 App1业务组件中再添加 <Route> 列表作为拼图占有区
路由会先匹配到外层父组件,然后再匹配父组件中Route组件。所以只要我想,我能一直套娃下去。
「路由用法」
- 所需 npm 包
npm install react-router
npm install react-router-dom
// 非必须
npm install react-router-config
「组件列表」
- Router
- StaticRouter
- MemoryRouter
- HashRouter
- BrowserRouter
- NativeRouter
- Switch
- Route
- Redirect
- Prompt
- Link
- NavLink
- withRouter
「组件详情」
-
Router
属性 说明 history staticContext children - BrowserRouter
属性 说明 basename forceRefresh getUserConfirmation keyLength children - HashRouter
属性 说明 basename getUserConfirmation hashType children -
Route
属性 说明 path component render children exact strict sensitive location -
Switch
属性 说明 location children -
Redirect
属性 说明 push from to -
Prompt
属性 说明 when message -
Link
属性 说明 innerRef replace target to onClick - NavLink (属性包含 Link )
属性 说明 activeClassName activeStyle className style exact isActive location sensitive strict