【React Router】React Router学习笔记

news/2025/2/19 8:31:15/

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.invoiceIdlet newId = this.props.params.invoiceIdif (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.ppmy.cn/news/1173132.html

相关文章

如何撰写一份优秀的活动策划与执行方案?这些步骤和技巧请收好

活动策划与执行是一个综合性、复杂性极高的工作&#xff0c;对于任何一场活动的成功举办都起着至关重要的作用。一份优秀的活动策划与执行方案能够确保活动目标的实现&#xff0c;有效管理资源&#xff0c;提升活动效果。在本文中&#xff0c;我们将探讨如何撰写一份优秀的活动…

【Java】Spring Cloud OAuth2之密码模式实战

Spring Cloud OAuth2 代码地址&#xff1a;https://gitee.com/kkmy/kw-microservices.git (又是一年1024&#xff0c;分享一下之前搭的OAuth2服务) OAuth2依赖版本 <dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud…

假脸检测:Exploring Decision-based Black-box Attacks on Face Forgery Detection

论文作者&#xff1a;Zhaoyu Chen,Bo Li,Kaixun Jiang,Shuang Wu,Shouhong Ding,Wenqiang Zhang 作者单位&#xff1a;Fudan University;Yiwu Research Institute of Fudan University 论文链接&#xff1a;http://arxiv.org/abs/2310.12017v1 内容简介&#xff1a; 1&…

docker和k8s之间的关系

一句话总结&#xff1a;Docker只是容器的一种&#xff0c;它面向的是单体&#xff0c;K8S可以管理多种容器&#xff0c;它面向的是集群&#xff0c;Docker可以作为一种容器方案被K8S管理。 https://baijiahao.baidu.com/s?id1763716289717819767&wfrspider&forpc 背…

kepler笔记:Trip

Trip图层可以显示动画路径 1 数据格式 目前Trip图层支持一种特殊的GeoJSON格式&#xff0c;其中坐标线串有一个表示时间戳的第4个元素 为了使路径动画化&#xff0c;GeoJSON数据需要在其特征的几何形状中包含LineString&#xff0c;并且LineString中的坐标需要有4个元素&…

JSX 动态类名控制

学习目标&#xff1a; 根据需求判断是否显示某个类名的样式 实现&#xff1a; 使用三元表达式或逻辑&&运算 import ./app.css; function App() {const color1 trueconst color2 truereturn (<div className"App">1. 三元&#xff1a;<div classN…

RK3568驱动指南|第七期-设备树-第59章 实例分析:CPU

瑞芯微RK3568芯片是一款定位中高端的通用型SOC&#xff0c;采用22nm制程工艺&#xff0c;搭载一颗四核Cortex-A55处理器和Mali G52 2EE 图形处理器。RK3568 支持4K 解码和 1080P 编码&#xff0c;支持SATA/PCIE/USB3.0 外围接口。RK3568内置独立NPU&#xff0c;可用于轻量级人工…

5分钟搞懂分布式可观测性

可观测性是大规模分布式(微服务)系统的必要组件&#xff0c;没有可观测系统的支持&#xff0c;监控和调试分布式系统将是一场灾难。本文讨论了可观测系统的主要功能&#xff0c;并基于流行的开源工具搭建了一套可观测系统架构。原文: A Primer on Distributed Systems Observab…