react-router不过是个智能换装游戏🧩

390 阅读4分钟

这是我参与更文挑战的第1天,活动详情查看: 更文挑战

「前言」

react-router 库其实也没什么好写,毕竟只要有一点 React 基础肯定就知道或者使用过 react-router。今天阅读 react-router 源码的时候突然想到一些好玩的东西。

这篇文章我记录我自己的心路历程,从根源上理解 react-router

「做个类比」

react 组件化的思想让开发者开发项目时就像玩「换装游戏」一样,把多个组件拼接成一个项目。有些情况,我们只是在某个位置替换不同的组件,而不需要整个页面都重新加载。

image-20210602230015507

利用 换装游戏 说明,人物角色的身体是固定好的,根据不同的设置,个性化的角色的帽子,发型,衣服,武器会会不一样。也就是在同一个位置,展示出了不同的内容。

类比到 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 导出的组件列表。

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:RouterContextHistoryContext

image-20201119184313852

RouterContext 给子组件消费者提供了 「history」「location」「match」「staticContext」四个属性。

HistoryContext 提供给子组件消费者提供了「history」属性。

Route 组件的作用是从 Router 组件的上下文中解构出属性,并同时组装自己props属性,然后新建一个 RouterContext 上下文并将props传递给业务组件。

image-20201119225039823

通过简单例子可以看出,从项目的入口列出 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>
   	...
  );
}
image-20201119231212160

嵌套代码只替换效果图中红框内容,只需要在 App1业务组件中再添加 <Route> 列表作为拼图占有区

路由会先匹配到外层父组件,然后再匹配父组件中Route组件。所以只要我想,我能一直套娃下去。

image-20201119232221120

「路由用法」

  • 所需 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