最新 React Router 全面整理

React router 已经到了 V5 版本,增加了基于 React Hooks 的一些 API,比如 useParams、useHistory 等等,让我们可以在组件中不接受 route props 就可以拿到路由信息 { match, location, location },除了利用了 React Hooks,React router 中还有其他充分展示了 React 特性的 API,比如 <Route render={}/> 利用了 render props,withRouter 利用了高阶组件。另外,就像 React 家族中的其他成员一样,React Router 也做了核心库和绑定库分离,在核心库 React Router 核心库,有绑定 web 端的 react-router-dom,绑定 React Native 的 react-router-native,还有用于集成 Redux 的 react-router-redux,最后,还有用于配置静态路由的 react-router-config

本文全面介绍了 React router 的使用方法和一些需要注意的问题,首发:https://qumuchegi.github.io/

本文参考

1. 使用说明

React router 只是一个核心库,在具体使用时应该基于不同的平台要使用不同的绑定库。比如:我们要在浏览器中使用 react router,就安装 react-router-dom 库,如果在 React Native 中使用 React router 就应该安装 react-router-native 库。但是我们不会安装 react-router.

2. 场景

2.1 响应式路由

响应手机、平板横向/纵向切换导致屏幕大小变换,从而动态改变路由界面布局,比如从纵向换到横向可以用更大的界面显示路由主从界面,将纵向时只能用新页面显示的子页面显示在和父页面相同的页面。

const App = () => (
  <AppLayout>
    <Route path="/invoices" component={Invoices} />
  </AppLayout>
);

const Invoices = () => (
  <Layout>
    {/* always show the nav */}
    <InvoicesNav />

    <Media query={PRETTY_SMALL}>
      {screenIsSmall =>
        screenIsSmall ? (
          // small screen has no redirect
          <Switch>
            <Route
              exact
              path="/invoices/dashboard"
              component={Dashboard}
            />
            <Route path="/invoices/:id" component={Invoice} />
          </Switch>
        ) : (
          // large screen does!
          <Switch>
            <Route
              exact
              path="/invoices/dashboard"
              component={Dashboard}
            />
            <Route path="/invoices/:id" component={Invoice} />
            <Redirect from="/invoices" to="/invoices/dashboard" />
          </Switch>
        )
      }
    </Media>
  </Layout>
);

(以上代码来自官网)

2.2 React router 和 Redux 集成

以便将路由信息同步到 Redux state 统一管理,并且路由组件可以从 Redux 获取路由信息,实现导航等功能。

集成好处:

1)路由信息可以同步到统一的 store 并可以从中获得

2)可以使用 Redux 的 dispatch action 来导航

3)集成 Redux 可以支持在 Redux devtools 中路由改变的时间履行调试

集成的必要性:

集成后允许 react router 的路由信息可以存到 redux ,所以就需要路由组件要能访问到 redux store,这样组件就可以使用 store 的 dispatch action,可以使用 dispatch 带上路由信息作为 action 的负载将路由信息存到 store,同时要能将路由信息从 Redux store 里面同步获取出来

集成方法有以下几种:
  1. 用 react-redux 的 <Provider store={store}> 包住 react router 的路由组件<Router>、<BrowserRouter>等,这样 react router 的路由组件就可以访问到 redux 的 store 了 ,被包裹的路由组件也就可以使用 store 的 dispatch 和 getState 与 Redux 交互。

    这种方法在编码时有以下两种模式:

    • (1). 用 <Provider> 包裹 <Router> 即用一个 <Provider> 一次性包裹所有路由页面,我在这里用的是这种:

       ReactDOM.render(
      
         <Provider  store={store}>
      
           <APPRouter/>
      
        </Provider>
      
        ,  document.getElementById('root'));
      
      • (2). 用 <Router> 的createElement属性,给每一个 <Route> 页面组件外面都包裹一个 <Provider>:
       const createElement  =  (component,props) => (
      
         return  (
      
          <Provider  store={store}>
      
             <Component  {...props} />
      
          </Provider>
      
         )
      
      )
      
      const Routes  =  () => (
      
        <Router  history={history}  createElement={createElement}  />
      
      )
  2. 第二种集成方法就是使用 react router 最新的 API withRouter,它可以将路由信息更新结果(match、history、location)传给它所包裹的组件,组件相当于是一个<Route> 组件了。使用 withRouter 集成 Redux 和 React router 的方法是:withRouter( connect( mapStateToProps, mapDispatchToProps ...))( wrappedComponent) ) ,这样被包裹组件既可以拿到 Redux 的 store (通过 dispatch 和 state),也可以拿到 React router 封装的 match、location、history 等路由信息,于是可以将路由信息与 redux store 集成,将路由信息同步到 store,或从 store 实时获取 路由信息。

    注意 ! ! :

    需要注意:withRouter 只是用来处理数据更新问题的。在使用一些 redux 的connect()或者 mobx的inject()的组件中,如果依赖于路由的更新要重新渲染,会出现路由更新了但是组件没有重新渲染的情况。这是因为 redux 和 mobx 的这些连接方法会修改组件的shouldComponentUpdate

    所以在使用 withRouter 解决更新问题的时候,一定要保证 withRouter 在最外层,比如withRouter(connect()(Component)),而不是 connect()(withRouter(Component))

2.3 代码分割

不用把整个应用程序下载下来,而是允许用户”增量下载“代码,可以减少首屏代码加载量,也可以避免后面重复下载相同部分的代码。React router 可以结合 webpack 和 @babel/plugin-syntax-dynamic-import、loadable-components 来实现代码分割。

一个 React router + webpack + @babel/plugin-syntax-dynamic-import + loadable-components 实现代码分割的简单示例:

.babelrc 配置文件:

{
  "prestes": ["@babel/presets-react"],
  "plugins": ["@babel/plugin-syntax-dynamic-import"]
}

@babel/plugin-syntax-dynamic-import 插件避免 babel 对动态 import 语法做过多的转化,允许 webpac 打包时将动态 import 的代码分离待单独的 bundle,实现代码分割。

使用 loadabel-component 和动态 import 懒下载组件:

import loadable from "@loadable/component";
import Loading from "./Loading.js";

const LoadableComponent = loadable(() => import("./Dashboard.js"), {
  fallback: <Loading />
});

export default class LoadableDashboard extends React.Component {
  render() {
    return <LoadableComponent />;
  }
}

2.4 滚动恢复

浏览器 history 新增的 scrollRestortation 属性支持页面恢复后自动滚动到之前滚动的位置。此属性有两个可选值(“auto" 自动和 "manual" 手动) ,默认自动就是滚动恢复。

所以,浏览器可以支持或者取消“滚动恢复”,只需设置 widnow.history.scrollRestoration 为 "auto"或者 “manual”即可。

但是我们也可以使用 react 的 useEffect 来解决:

import { useEffect } from "react";

function ScrollToTopOnMount() {
  useEffect(() => {
    window.scrollTo(0, 0);
  }, []);

  return null;
}

// Render this somewhere using:
// <Route path="..." children={<LongContent />} />
function LongContent() {
  return (
    <div>
      <ScrollToTopOnMount />

      <h1>Here is my long content page</h1>
      <p>...</p>
    </div>
  );
}

我们甚至可以随意控制页面恢复时滚动到何处。只需把页面需要滚动的位置存到 sessionStorage,然后页面可以从 sessionStorage 获取要滚动位置即可。

2.5 服务端渲染

使用服务端渲染路由时添加状态。SPA 单页面应用通常都是使用客户端路由,但是有时使用服务端渲染可以对应用的 SEO 更好,所以可以使用服务端渲染。但是,使用服务端渲染有一个问题就是,它不像客户端渲染那样可以对前后两个请求进行关联,也就是说服务端渲染是无状态的、静态的。在使用 React router 做服务端渲染的路由时,如果我们在跳转到另外一个页面时需要传递一些信息以便于根据这些信息做出不同响应,也就是让它具备状态,那么我们可以使用 <StaticRouter> 的 context ,在context 装载信息,比如 http 状态吗(301,302 等等)、url。

一个在 react router 应用中让服务端渲染具备状态的例子:

服务端:

import http from "http";
import React from "react";
import ReactDOMServer from "react-dom/server";
import { StaticRouter } from "react-router-dom";

import App from "./App.js";

http
  .createServer((req, res) => {
    const context = {};

    const html = ReactDOMServer.renderToString(
      <StaticRouter location={req.url} context={context}>
        <App />
      </StaticRouter>
    );

    if (context.url) {
      res.writeHead(301, {
        Location: context.url
      });
      res.end();
    } else {
      res.write(`
      <!doctype html>
      <div id="app">${html}</div>
    `);
      res.end();
    }
  })
  .listen(3000);

客户端:

import ReactDOM from "react-dom";
import { BrowserRouter } from "react-router-dom";

import App from "./App.js";

ReactDOM.render(
  <BrowserRouter>
    <App />
  </BrowserRouter>,
  document.getElementById("app")
);

2.6 嵌套路由

1)可以使用在子路由组件里再次使用路由器<Router/><BrowserRouter/><HashRouter/>(下面将会介绍)的方法。

2)使用以下的静态路由配置库 react-router-config,可以在配置 routes 对象里面嵌套多层路由。

2.7 静态路由

静态路由是在应用运行之前就固定好了路由结构。

可以使用react-router-config 这个库来静态配置路由:

const routes = [
  {
    component: Root,
    routes: [
      {
        path: "/",
        exact: true,
        component: Home
      },
      {
        path: "/child/:id",
        component: Child,
        routes: [
          {
            path: "/child/:id/grand-child",
            component: GrandChild
          }
        ]
      }
    ]
  }
];

react-router-config API

  • renderRoutes(routes, extraProps = {}, switchProps = {}):这个ApI把路由组件渲染出来。routes 即是上面配置的可嵌套路由配置对象。
  • matchRoutes(routes, pathname):这个API返回被匹配的路由。使用它可以在应用渲染路由匹配的组件之前做一些操作。
import { matchRoutes, renderRoutes } from "react-router-config"

const routes = [
  {
    component: Root,
    routes: [
      {
        path: '/',
        component: Home,
        exact: true
      },
      {
        path: '/article/:articleTitle',
        component: Article,
      },
      {
        path: '/project',
        component: Project,
      },
      {
        component: NoMatch
      }
    ]
  }
]

function Root({route}) {
  return(
    <div>
      {renderRoutes(route.routes)}
    </div>
  )
}

2.8 动态路由

与静态路由不同,动态路由可以动态改变路由,在应用运行的时候可以动态改变路由结构,也就是在运行时可以动态改变UI与路由的映射关系。

使用编码实现即可,如条件渲染路由器,或者使用纯函数返回路由器。

3. React router API

3.1 路由器组件

<BrowserRouter><HashRouter><MemoryRouter><NativeRouter><StaticRouter><Router>

3.2 Hooks

  • useHistory

    返回 react router 封装的 history 实例,然后我们可以使用它来导航。

    注意,这里返回的 history 实例是指 React router 封装的实例,而它有来自 history 包。与浏览器的 history 不同:

    react router 的 history:

window history:

  • useLocation

    返回 react router 封装的 location 实例

    这里的 location 也与浏览器的 location 不一样

    react router的 location:

       {
         key: 'ac3df4', // not with HashHistory!
         pathname: '/somewhere',
         search: '?some=search-string',
         hash: '#howdy',
     [userDefined]: true
   }
 }

window.location:

react router 提供 location 对象的地方

  • Route 组件接收到的 props 中可以得到 location
  • Route 组件的属性 render 的回调函数的参数 : ({ location }) => ()
  • Route 组件的属性 children 的回调函数的参数: ({ location }) => ()
  • withRouter 传递给所包裹组件中 props 包含 locaton

location 可以用于:

   <Link to={location}/>
   <Redirect to={location}/>
   history.push(location)
   history.replace(location)
  • useParams

用以获取 match.params

import React from "react";
import ReactDOM from "react-dom";
import {
  BrowserRouter as Router,
  Switch,
  Route,
  useParams
} from "react-router-dom";

function BlogPost() {
  let { slug } = useParams();
  return <div>Now showing post {slug}</div>;
}

ReactDOM.render(
  <Router>
    <Switch>
      <Route exact path="/">
        <HomePage />
      </Route>
      <Route path="/blog/:slug">
        <BlogPost />
      </Route>
    </Switch>
  </Router>,
  node
);

(此代码来自官网)

  • useRouteMatch

    用与 <Route> 一样的方式匹配当前 URL,但是不会渲染对应的组件,只是返回 match

3.3 导航器 (就是链接)

3.4 路由匹配器

3.5 winthRouter

这是一个高阶组件(函数),withRouter 可以包装任何自定义组件,将 react-router 的 history,location,match 三个对象传入。 无需一级级传递react-router 的属性,当需要用的router 属性的时候,将组件包一层 withRouter,就可以拿到需要的路由信息,可以作为一种集成 Redux 和 react router 的方法。

Image placeholder
Kingfree
未设置
  45人点赞

没有讨论,发表一下自己的看法吧

推荐文章
Twitter 宣布抛弃 Mesos,全面转向 Kubernetes

作者|阿里云智能高级技术专家张磊划重点Twitter的基础设施从Mesos全面转向Kubernetes阿里云容器平台团队即将开源 Kubernetes高级作业管理集合美国西部时间5月2日下午7点,Tw

拐点已至,阿里云进入All in Cloud全面上云时代!

当新旧设备交替势不可挡,当云的基础设施开始超过传统的数据中心,一个新时代开始了。在这样的时代里,以数据、计算、智能为主要特征的新一代信息技术,开始成为企业数字化转型的核心动力。对于所有云服务商来说,就

怎么安装react-router

怎么安装react-router安装命令:npminstallreact-routerreact-router路由提供了一些router的核心api,包括Router,Route,Switch等,但是

PostgreSQL 12 正式发布:全面的性能提升

PostgreSQL12已经发布,该版本在各方面都得到了加强,包括显著地提升查询性能,特别是对大数据集,总的空间利用率方面。这个版本为应用程序开发人员提供了更多的功能,比如对SQL/JSON路径表达式

重回榜首!Facebook开源加强版BERT,全面超越XLNet

大数据文摘出品作者:宁静刚刚被拉下神坛的BERT又一次称霸了GLUE、SQuAD和RACE三个排行榜。今年六月,谷歌发布XLNet,指出并解决了BERT的缺点,在20多个指标上全面刷爆了BERT之前的

GoldenDB ,一个已经全面支撑银行核心系统的国产数据库

摘要:沿用、并存还是替代,一直是银行核心系统数据库转型重点思考的问题。四大行目前主要采用的是沿用与并存的数据库产品战略,在确保稳定的大前提下对新兴数据库技术进行探索研究和实践。相对而言,股份制银行在这

阿里云为什么有底气喊出“全面上云的拐点到了”?

摘要:拐点,又称反曲点,在高等数学里,拐点指的是凹凸性变化的点,在生活中借指趋势发生变化的点。(例如:经济运行出现回升拐点)虽然上云是大势所趋,但企业全面上云可能还需要一些时日,这或许是绝大部分从业者

全面集成阿里云,Salesforce在中国正式转正

进入2019年,SaaS竞争已进入深水区,当很多CRM厂商还在庆幸Salesforce在中国没有准入资格时,Salesforce已经在悄然布局,曲线入市。7月25日,在阿里云峰会·上海站上,全球最大C

商务办公的理想之选,联想ThinkPad L490全面评测

对于职场精英们来说,一台稳定耐用、功能强大的商务本可谓是工作中的必须品,在高端商务本市场中的代表品牌当属ThinkPad,ThinkPad的很多条产品线都可称得上商务本市场中的标杆级产品!不过Thin

DTCC2019数据库技术评选结果全面揭晓,四大类别奖项新鲜出炉!

数据风云,十年变迁,而DTCC是一切变革的见证者。作为数据库领域顶尖级的盛会,DTCC已经成功举办了九届,今年是第十届。继承往届会议规模大、涉及话题更前沿、演讲内容更深入等特点之外,DTCC2019数

等保2.0全面实施 新华三支招如何构建新形势下的态势感知

近年来态势感知的热度非常高,不少安全厂商也纷纷推出了态势感知(平台)。在今年五月份,我国正式推出了等保2.0,并已于12月1日正式实施。在等保2.0的安全框架中也明确提出了要具备态势感知的能力,要

阿里云正式推出内容平台“云栖号”:全面助力企业和个人上云决策

1月7日,阿里云官网正式推出“云栖号”(https://yqh.aliyun.com/ ),旨在为大家提供第一手的上云资讯,云产品快速入门,来自不同行业精选的企业上云案例,基于众多成功案例萃取而成的最

全面了解 Nginx 主要应用场景

前言本文只针对Nginx在不加载第三方模块的情况能处理哪些事情,由于第三方模块太多所以也介绍不完,当然本文本身也可能介绍的不完整,毕竟只是我个人使用过和了解到过得。所以还请见谅,同时欢迎留言交流Ngi

详解IBM i 操作环境的最新版本IBM i 7.4 和新特许程序 IBM Db2 Mirror for i

一、背景随着行业客户大规模上云的趋势越来越明显,Power在企业云化中将扮演越来越重要的角色。过去一年,浪潮商用机器基于开放的Power技术的服务器,完善服务器的生态系统,建立可持续发展的服务器业务,

Go语言高级编程_5.2 router 请求路由

5.2router请求路由 在常见的Web框架中,router是必备的组件。Go语言圈子里router也时常被称为http的multiplexer。在上一节中我们通过对Burrow代码的简单学习,已经

Spring Boot自动装配整理

首先写一个我们自己的HelloWorld配置类 1、基于"注解驱动"实现@Enable模块 @ConfigurationpublicclassHelloWorldConfiguration{@Bean

Xcode调试、性能优化基本工具使用简单整理

断点1.普通断点在行号那儿点一下就加上了,最常用的断点,略。2.条件断点很多时候问题代码是被高频调用直到特定条件下才出现问题的,这种时候可以使用条件断点。在任意断点右击选择EditBreakpoint

【MySQL实战45讲】索引部分整理

本文摘抄自极客时间【MySQL实战45讲】为什么要有索引?索引的作用是什么?索引的出现其实就是为了提高数据查询的效率,就像书的目录一样。一本书我们可以通过目录中快速的定位其中的某一个知识点;对于数据库

基于JS的高性能Flutter动态化框架MXFlutter

导语:18年10月份,手机QQ看点团队尝试使用Flutter,做为iOS开发,一接触到Flutter就马上感受到,Flutter虽然强大,但不能像RN一样动态化是阻碍我们使用她的唯一障碍了。看Goog

最新的 Composer 包镜像地址

最新的包地址 composerconfig-grepo.packagistcomposerhttps://mirrors.aliyun.com/composer/

使用Certbot开启HTTPS访问(最新)

知乎地址友情链接 v-easy-components-基于Vue2.x的组件命令库 逸宿-一款预定民宿的webapp(毕设) 在线网易云API-基于NeteaseCloudMusicApi在线A

如何在浏览器中获取 Production Mode 的 React 实例

https://github.com/LiuuY/Blog...在ProductionMode下,React并没有暴露其实例。无论什么原因如果你要获取的话可以参考以下方法。条件浏览器安装了ReactD

IBM Spectrum Protect 8.1.7在AIX7.1上的安装和配置

                                                本文作者: 谷铁柏摘要:    本文章主要讲述IBMSpectrumProtect8.1.7版本在AIX

最新安卓零日漏洞被曝出,或影响谷歌、华为和小米等品牌手机

近日,据外媒BleepingComputer报道,谷歌威胁分析团队(TAG)称,一个最新的安卓0day漏洞或被用于攻击谷歌Pixel、华为、小米和三星以及OPPO等智能手机。 该漏洞是由谷歌Pro

2019 最新计算机技能排名出炉:Python 排第三,第一名是…

除了编程语言之外,要想找一份计算机相关的工作,还需要很多其他方面的技能。最近,来自美国求职公司Indeed的一份报告显示:在全美工作技能需求中,数据库语言SQL、编程语言Java分列前两位。虽然Pyt

{