【React Router】React Router学习笔记

news2024/12/29 10:45:28

React Router学习笔记

  • React Router
    • 1.什么是React Router?
    • 2.为什么要用React Router?
    • 3.基础
      • 3.1 路由配置
      • 3.2 路由匹配原理
      • 3.3 History
        • 3.3.1 browerHistory
        • 3.3.2 hashHistory
        • 3.3.3 createMemoryHistory
        • 3.3.4 实现示例
      • 3.4 默认路由(IndexRoute)与IndexLink
        • 3.4.1 IndexRoute
        • 3.4.2 IndexLink
    • 4.高级用法
      • 4.1 动态路由
      • 4.2 跳转前确认
      • 4.3 服务端渲染
      • 4.4 组件生命周期
      • 4.5 组件外部跳转

React Router

在这里插入图片描述

1.什么是React Router?

React Router 是一个基于 React 之上的强大路由库,它可以让你向应用中快速地添加视图和数据流,同时保持页面与 URL 间的同步。

2.为什么要用React Router?

  1. React Router 知道如何为我们搭建嵌套的 UI,因此我们不用手动找出需要渲染哪些 <Child> 组件。
  2. 获取URL参数。当渲染组件时,React Router 会自动向 Route 组件中注入一些有用的信息,尤其是路径中动态部分的参数。

3.基础

3.1 路由配置

路由配置是一组指令,用来告诉 router 如何匹配 URL以及匹配后如何执行代码。

示例:

import React from 'react'
import { Router, Route, Link } from 'react-router'

const App = React.createClass({
  render() {
    return (
      <div>
        <h1>App</h1>
        <ul>
          <li><Link to="/about">About</Link></li>
          <li><Link to="/inbox">Inbox</Link></li>
        </ul>
        {this.props.children}
      </div>
    )
  }
})

const About = React.createClass({
  render() {
    return <h3>About</h3>
  }
})

const Inbox = React.createClass({
  render() {
    return (
      <div>
        <h2>Inbox</h2>
        {this.props.children || "Welcome to your Inbox"}
      </div>
    )
  }
})

const Message = React.createClass({
  render() {
    return <h3>Message {this.props.params.id}</h3>
  }
})

React.render((
  <Router>
    <Route path="/" component={App}>
      <Route path="about" component={About} />
      <Route path="inbox" component={Inbox}>
        <Route path="messages/:id" component={Message} />
      </Route>
    </Route>
  </Router>
), document.body)

可以使用IndexRoute给路径/添加默认首页

import { IndexRoute } from 'react-router'

const Dashboard = React.createClass({
  render() {
    return <div>Welcome to the app!</div>
  }
})

React.render((
  <Router>
    <Route path="/" component={App}>
      {/* 当 url 为/时渲染 Dashboard */}
      <IndexRoute component={Dashboard} />
      <Route path="about" component={About} />
      <Route path="inbox" component={Inbox}>
        <Route path="messages/:id" component={Message} />
      </Route>
    </Route>
  </Router>
), document.body)

现在,Apprender 中的 this.props.children 将会是 <Dashboard>这个元素。

现在的sitemap如下所示:

URL组件
/App -> Dashboard
/aboutApp -> About
/inboxApp -> Inbox
/inbox/messages/:idApp -> Inbox -> Message

绝对路径可以将 /inbox/inbox/messages/:id 中去除,并且还能够让 Message 嵌套在 App -> Inbox 中渲染。

React.render((
  <Router>
    <Route path="/" component={App}>
      <IndexRoute component={Dashboard} />
      <Route path="about" component={About} />
      <Route path="inbox" component={Inbox}>
        {/* 使用 /messages/:id 替换 messages/:id */}
        <Route path="/messages/:id" component={Message} />
      </Route>
    </Route>
  </Router>
), document.body)

在多层嵌套路由中使用绝对路径使我们无需在 URL 中添加更多的层级,从而可以使用更简洁的 URL。

绝对路径可能在动态路由中无法使用。

上面的修改使得URL发生了改变,我们可以使用Redirect来兼容旧的URL。

import { Redirect } from 'react-router'

React.render((
  <Router>
    <Route path="/" component={App}>
      <IndexRoute component={Dashboard} />
      <Route path="about" component={About} />
      <Route path="inbox" component={Inbox}>
        <Route path="/messages/:id" component={Message} />

        {/* 跳转 /inbox/messages/:id 到 /messages/:id */}
        <Redirect from="messages/:id" to="/messages/:id" />
      </Route>
    </Route>
  </Router>
), document.body)

3.2 路由匹配原理

路由拥有三个属性来决定是否“匹配“一个 URL:

  1. 嵌套关系

    嵌套路由被描述成一种树形结构。React Router 会深度优先遍历整个路由配置来寻找一个与给定的 URL 相匹配的路由。

  2. 路径语法

    :paramName – 匹配一段位于 /?# 之后的 URL。 命中的部分将被作为一个参数
    () – 在它内部的内容被认为是可选的

    *– 匹配任意字符(非贪婪的)直到命中下一个字符或者整个 URL 的末尾,并创建一个 splat 参数

    <Route path="/hello/:name">         // 匹配 /hello/michael 和 /hello/ryan
    <Route path="/hello(/:name)">       // 匹配 /hello, /hello/michael 和 /hello/ryan
    <Route path="/files/*.*">           // 匹配 /files/hello.jpg 和 /files/path/to/hello.jpg
    

    如果一个路由使用了相对路径,那么完整的路径将由它的所有祖先节点的路径和自身指定的相对路径拼接而成。使用绝对路径可以使路由匹配行为忽略嵌套关系。

  3. 优先级

    路由算法会根据定义的顺序自顶向下匹配路由。因此,当拥有两个兄弟路由节点配置时,必须确认前一个路由不会匹配后一个路由中的路径。例如:

    <Route path="/comments" ... />
    <Redirect from="/comments" ... />
    

3.3 History

一个 history 知道如何去监听浏览器地址栏的变化, 并解析这个 URL 转化为 location 对象, 然后 router 使用它匹配到路由,最后正确地渲染对应的组件。

常用的 history 有三种形式, 但也可以使用 React Router 实现自定义的 history

  • browserHistory
  • hashHistory
  • createMemoryHistory
3.3.1 browerHistory

Browser history 是使用 React Router 的应用推荐的 history。它使用浏览器中的 History API 用于处理 URL,创建一个像example.com/some/path这样真实的 URL 。

3.3.2 hashHistory

Hash history 使用 URL 中的 hash(#)部分去创建形如 example.com/#/some/path 的路由。

3.3.3 createMemoryHistory

Memory history 不会在地址栏被操作或读取。同时它也非常适合测试和其他的渲染环境(像 React Native )。这种history需要创建。

const history = createMemoryHistory(location)
3.3.4 实现示例
import React from 'react'
import { render } from 'react-dom'
import { browserHistory, Router, Route, IndexRoute } from 'react-router'

import App from '../components/App'
import Home from '../components/Home'
import About from '../components/About'
import Features from '../components/Features'

render(
	<Router history={browserHistory}>
        <Route path='/' component={App}>
            <IndexRoute component={Home} />
            <Route path='about' component={About} />
            <Route path='features' component={Features} />
        </Route>
    </Router>,
    document.getElementById('app')
)

3.4 默认路由(IndexRoute)与IndexLink

3.4.1 IndexRoute

当用户访问 / 时, App 组件被渲染,但组件内的子元素却没有, App 内部的 this.props.children 为 undefined 。Home 无法参与到比如 onEnter hook 这些路由机制中来。 在 Home 的位置,渲染的是 AccountsStatements。 router 允许使用 IndexRoute ,以使 Home 作为最高层级的路由出现。

<Router>
  <Route path="/" component={App}>
    <IndexRoute component={Home}/>
    <Route path="accounts" component={Accounts}/>
    <Route path="statements" component={Statements}/>
  </Route>
</Router>

现在 App 能够渲染 {this.props.children} 了, 也有了一个最高层级的路由,使 Home 可以参与进来。

3.4.2 IndexLink

如果需要在 Home 路由被渲染后才激活的指向 / 的链接,请使用 <IndexLink to="/">Home</IndexLink>

4.高级用法

4.1 动态路由

React Router 里的路径匹配以及组件加载都是异步完成的,不仅允许延迟加载组件,并且可以延迟加载路由配置。

React Router 会逐渐的匹配 URL 并只加载该 URL 对应页面所需的路径配置和组件。

结合Webpack可以在路由发生改变时,资源按需加载。

const CourseRoute = {
  path: 'course/:courseId',

  getChildRoutes(location, callback) {
    require.ensure([], function (require) {
      callback(null, [
        require('./routes/Announcements'),
        require('./routes/Assignments'),
        require('./routes/Grades'),
      ])
    })
  },

  getIndexRoute(location, callback) {
    require.ensure([], function (require) {
      callback(null, require('./components/Index'))
    })
  },

  getComponents(location, callback) {
    require.ensure([], function (require) {
      callback(null, require('./components/Course'))
    })
  }
}

4.2 跳转前确认

React Router 提供一个 routerWillLeave 生命周期钩子,这使得 React 组件可以拦截正在发生的跳转,或在离开 route 前提示用户。routerWillLeave 返回值有以下两种:

  1. return false 取消此次跳转
  2. return 返回提示信息,在离开 route 前提示用户进行确认。

可以在 route 组件 中引入 Lifecycle mixin 来安装这个钩子。

import { Lifecycle } from 'react-router'

const Home = React.createClass({

  // 假设 Home 是一个 route 组件,它可能会使用
  // Lifecycle mixin 去获得一个 routerWillLeave 方法。
  mixins: [ Lifecycle ],

  routerWillLeave(nextLocation) {
    if (!this.state.isSaved)
      return 'Your work is not saved! Are you sure you want to leave?'
  },

  // ...

})

推荐使用 React.createClass 来创建组件,初始化路由的生命周期钩子函数。

如果想在一个深层嵌套的组件中使用 routerWillLeave 钩子,只需在 route 组件 中引入 RouteContext mixin,这样就会把 route 放到 context 中。

import { Lifecycle, RouteContext } from 'react-router'

const Home = React.createClass({

  // route 会被放到 Home 和它子组件及孙子组件的 context 中,
  // 这样在层级树中 Home 及其所有子组件都可以拿到 route。
  mixins: [ RouteContext ],

  render() {
    return <NestedForm />
  }

})

const NestedForm = React.createClass({

  // 后代组件使用 Lifecycle mixin 获得
  // 一个 routerWillLeave 的方法。
  mixins: [ Lifecycle ],

  routerWillLeave(nextLocation) {
    if (!this.state.isSaved)
      return 'Your work is not saved! Are you sure you want to leave?'
  },

  // ...

})

4.3 服务端渲染

服务端渲染与客户端渲染有些许不同,因为你需要:

  • 发生错误时发送一个 500 的响应

  • 需要重定向时发送一个 30x 的响应

  • 在渲染之前获得数据 (用 router 完成这点)

为了迎合这一需求,要在 API 下一层使用:

  • 使用 match 在渲染之前根据location 匹配 route
  • 使用 RoutingContext 同步渲染 route 组件
import { renderToString } from 'react-dom/server'
import { match, RoutingContext } from 'react-router'
import routes from './routes'

serve((req, res) => {
  // 注意!这里的 req.url 应该是从初始请求中获得的
  // 完整的 URL 路径,包括查询字符串。
  match({ routes, location: req.url }, (error, redirectLocation, renderProps) => {
    if (error) {
      res.send(500, error.message)
    } else if (redirectLocation) {
      res.redirect(302, redirectLocation.pathname + redirectLocation.search)
    } else if (renderProps) {
      res.send(200, renderToString(<RoutingContext {...renderProps} />))
    } else {
      res.send(404, 'Not found')
    }
  })
})

4.4 组件生命周期

假如路由配置如下:

<Route path="/" component={App}>
  <IndexRoute component={Home}/>
  <Route path="invoices/:invoiceId" component={Invoice}/>
  <Route path="accounts/:accountId" component={Account}/>
</Route>

路由切换时,组件生命周期的变化情况如下:

  1. 当用户打开应用的/页面

    组件生命周期
    AppcomponentDidMount
    HomecomponentDidMount
    InvoiceN/A
    AccountN/A
  2. 当用户从/跳转到/invoice/123

    组件生命周期
    AppcomponentWillReceiveProps,componentDidUpdate
    HomecomponentWillUnmount
    InvoicecomponentDidMount
    AccountN/A
    • App 从 router 中接收到新的 props(例如 childrenparamslocation 等数据), 所以 App 触发了 componentWillReceivePropscomponentDidUpdate 两个生命周期方法
    • Home 不再被渲染,所以它将被移除
    • Invoice 首次被挂载
  3. 当用户从/invoice/123跳转到/invoice/789

    组件生命周期
    AppcomponentWillReceiveProps,componentDidUpdate
    HomeN/A
    InvoicecomponentWillReceiveProps,componentDidUpdate
    AccountN/A

    所有的组件之前都已经被挂载, 所以只是从 router 更新了 props.

  4. 当从/invoice/789跳转到/accounts/123

    组件生命周期
    AppcomponentWillReceiveProps,componentDidUpdate
    HomeN/A
    InvoicecomponentWillUnmount
    AccountcomponentDidMount

虽然还有其他通过 router 获取数据的方法, 但是最简单的方法是通过组件生命周期 Hook 来实现。示例如下,在 Invoice 组件里实现一个简单的数据获取功能。

let Invoice = React.createClass({

  getInitialState () {
    return {
      invoice: null
    }
  },

  componentDidMount () {
    // 上面的步骤2,在此初始化数据
    this.fetchInvoice()
  },

  componentDidUpdate (prevProps) {
    // 上面步骤3,通过参数更新数据
    let oldId = prevProps.params.invoiceId
    let newId = this.props.params.invoiceId
    if (newId !== oldId)
      this.fetchInvoice()
  },

  componentWillUnmount () {
    // 上面步骤四,在组件移除前忽略正在进行中的请求
    this.ignoreLastFetch = true
  },

  fetchInvoice () {
    let url = `/api/invoices/${this.props.params.invoiceId}`
    this.request = fetch(url, (err, data) => {
      if (!this.ignoreLastFetch)
        this.setState({ invoice: data.invoice })
    })
  },

  render () {
    return <InvoiceView invoice={this.state.invoice} />
  }
})

4.5 组件外部跳转

虽然在组件内部可以使用 this.context.router 来实现导航,但许多应用想要在组件外部使用导航。使用Router组件上被赋予的history可以在组件外部实现导航。

// your main file that renders a Router
import { Router, browserHistory } from 'react-router'
import routes from './app/routes'
render(<Router history={browserHistory} routes={routes}/>, el)
// somewhere like a redux/flux action file:
import { browserHistory } from 'react-router'
browserHistory.push('/some/path')

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1125862.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

javaEE -6(10000详解文件操作)

一&#xff1a;认识文件 我们先来认识狭义上的文件(file)。针对硬盘这种持久化存储的I/O设备&#xff0c;当我们想要进行数据保存时&#xff0c;往往不是保存成一个整体&#xff0c;而是独立成一个个的单位进行保存&#xff0c;这个独立的单位就被抽象成文件的概念&#xff0c…

畅行全球,美格智能SLM750模组锻造出海核心优势

什么是产品认证制度&#xff1f; 国际标准化组织&#xff08;ISO&#xff09;将产品认证定义为&#xff1a;由第三方通过检验评定企业的质量管理体系和样品型式试验来确认企业的产品、过程或服务是否符合特定要求&#xff0c;是否具备持续稳定地生产符合标准要求产品的能力&am…

蓝桥每日一题(day 5: 蓝桥593.既约分数)--数学--easy(注:排掉一个重复的情况)

考察gcd模板求解最大公约数。由于我是2去做的&#xff0c;实际上当ij1的时候&#xff0c;能构成的分数只能是一种情况&#xff0c;所以最后的res需要减去1&#xff01;&#xff01;&#xff01; #include <iostream> using namespace std;int gcd(int a, int b){return …

【Bayes-LSTM预测】基于贝叶斯优化算法优化长短期记忆网络的数据分类预测研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

建材物料经营小程序商城的作用是什么

对商家而言&#xff0c;入驻到第三方平台&#xff0c;除了受平台各种限制外&#xff0c;还有佣金/抽成等&#xff0c;也不利于打造私域流量池及会员管理、营销、转化、复购裂变留存等&#xff0c;只能依赖平台活动进行经营。 如今线下流量匮乏及难获取&#xff0c;发传单口口相…

【算法设计】递归与分治算法设计——二分搜索、假币识别问题(C++实现)

创作不易&#xff0c;本篇文章如果帮助到了你&#xff0c;还请点赞 关注支持一下♡>&#x16966;<)!! 主页专栏有更多知识&#xff0c;如有疑问欢迎大家指正讨论&#xff0c;共同进步&#xff01; 更多算法分析与设计知识专栏&#xff1a;算法分析&#x1f525; 给大家跳…

一个对接第三方会员充值平台的小程序方案

偶尔看到淘宝网上腾讯会员充值等服务卖的很火,所以在找有没有第三方平台的充值接口可以用呢,还真找到了,做简单的对接,前端VUE,后端springBoot抓取会员商品列表。 由于个人未开通支付渠道,不能进一步对接支付,分享出来,有兴趣的同学可以继续对接支付商用。 后端代码,主…

【JAVA学习一:基础语法】

记录学习过程和代码编写&#xff0c;小白纯属记录。 目录 一、运算符 二、数组 三、面向对象 一、运算符 赋值运算符 public class Demo01 { public static void main(String[] args){ System.out.println(11); System.out.println(1-1); System.out.printl…

Java8 BiConsumer<T, U> 函数接口浅析分享(含示例,来戳!)

文章目录 Java8 BiConsumer<T, U> 函数接口浅析分享&#xff08;含示例&#xff0c;来戳&#xff01;&#xff09;源码accept 方法示例示例一示例二 andThen 方法示例示例一示例二 示例相关代码类dohandler 方法student.javaStudentScore.javaStudentScoreDto.java Java8…

Redbook Chapter 7: Query Optimization翻译批注

首先说明一下redbook上的几篇文章是做什么的。这几篇文章是通过几位作者对不同方面的论文进行阅读和筛选后&#xff0c;挑出其中具备代表性或者权威的论文来做分析&#xff0c;为读者提供阅读指导和建议&#xff0c;同时&#xff0c;也是对某个方面的论文进行高度的总结&#x…

决策树完成图片分类任务

数据集要求&#xff1a; 训练集 和 验证集 &#xff08;要求分好&#xff09; 图片放置规则 &#xff1a; 一个总文件夹 放类别名称的子文件夹 其中子文件夹 为存放同一类别图片 举个例子 分类动物 则 总文件夹名称为动物 子文件夹为 猫 狗 猪猪 。。。 其中猫的文件夹里面…

关于设置图标

1. exe图标 visual studio给编译的exe程序添加程序图标的方法_vs编译的exe图标-CSDN博客 2.窗口图标和任务栏图标 setWindowIcon 3.任务管理器的图标 外部是exe的图标&#xff0c;内部是窗口图标。

更改idea的JDK版本

有时候我们需要更改 idea 的 JDK 版本&#xff0c;这里告诉大家更改的方法&#xff0c;非常简单快捷&#xff0c;而且也不需要去找 JDK 的资源 1.在 idea 的左上角找到 File 选择 Peoject Structure 2.在页面左上角找到 Project &#xff0c;点击 SDK 的框&#xff0c;选择 A…

动态规划之买卖股票全解析【通俗易懂】

文章目录 前言一、无限制数1、无限次买入卖出且无手续费2、无限次买入卖出且无手续费&#xff0c;但是有冷冻期3、无限次买入卖出但是有手续费4、只能买卖一次 二、有限制数 前言 买卖股票问题是动态规划中最经典的问题我把这一类问题分为两大类。一类是没有限制的&#xff0c…

【java源码】医院绩效考核系统源码 支持主流的“成本法”、“工作量法”、“平衡计分卡法”的绩效方案

医院绩效考核系统源码 &#xff0c;&#xff08;有项目应用案例&#xff09;可适应医院多种绩效核算方式。 医院绩效考核管理系统是采用B/S架构模式设计、使用JAVA语言开发、后台使用MySql数据库进行管理的一整套计算机应用软件。系统和his系统进行对接&#xff0c;按照设定周期…

Node编写用户注册接口

目录 前言 创建服务器 编写注册接口API 创建路由对象&#xff0c;将路由对象导出去 将路由对象导出到服务器中 判断用户发起注册请求时是否输入账号或密码 验证表单数据 在数据库中创建表 在node中绑定mysql数据库 判断用户注册的账号密码是否已经被注册 密码加密 完…

Redis详细安装教程

一、Redis 的安装及启动停止 1-1 下载 redis的压缩包 wget https://download.redis.io/releases/redis-5.0.14.tar.gz1-2 开始解压 redis tar -zxvf redis-5.0.14.tar.gz1-3 执行 make 命令编译 make PREFIX/usr/redis install &#xff08;如果不加prefix 默认安装到/usr/…

Java IDEA feign调用上传文件MultipartFile以及实体对象亲测可行

Java IDEA feign调用上传文件MultipartFile以及实体对象亲测可行 1. 报错 java.lang.IllegalStateException: Body parameter cannot be used with form parameters2. 解决参考 1. 报错 java.lang.IllegalStateException: Body parameter cannot be used with form parameters …

【API篇】六、Flink输出算子Sink

文章目录 1、输出到外部系统2、输出到文件3、输出到KafKa4、输出到MySQL&#xff08;JDBC&#xff09;5、自定义Sink输出 Flink做为数据处理引擎&#xff0c;要把最终处理好的数据写入外部存储&#xff0c;为外部系统或应用提供支持。与输入算子Source相对应的&#xff0c;输出…

docker部署rabbitmq的坑

背景 今天用docker部署rabbitmq&#xff0c;启动都一起正常&#xff0c;但是当访问15672端口时&#xff0c;不能加载出页面。 排查 1.防火墙是否开启 ufw status2.ip是否能ping通 ping 192.168.x.x3.检查docker日志 docker psdocker logs -f 容器id4.进入容器&#xff0c…