[React路由]

React路由

写在前面:此笔记是跟着尚硅谷课程视频所写

一. SPA的理解

1.单页Web应用(single page web application,SPA)。
2.整个应用只有一个完整的页面。
3.点击页面中的链接不会刷新页面,只会做页面的局部更新
4.数据都需要通过ajax请求获取, 并在前端异步展现。

总结下来就是 “单页面多组件”

二. 路由的理解

什么是路由

1.一个路由就是一个映射关系(key:value)
2.key为路径 path, value可能是function或component

路由分类

1.后端路由:

1)理解: value是function, 用来处理客户端提交的请求。
2)注册路由: router.get(path, function(req, res))
3)工作过程:当node接收到一个请求时, 根据请求路径找到匹配的路由key , 调用路由中的函数来处理请求, 返回响应数据value

2.前端路由:

1)浏览器端路由,value是component,用于展示页面内容。
2)注册路由:
3)工作过程:当浏览器的path变为/test时, 当前路由组件就会变为Test组件

三、路由的基本使用

1. react-router-dom相关API

1.<BrowserRouter>
2.<HashRouter>
3.<Route>
4.<Redirect>
5.<Link>
6.<NavLink>
7.<Switch>
  1. 安装react-router-dom
npm i react-router-dom@5
  1. 导入路由
// 可以导入API中的任何一个
import {Link,Route} from 'react-router-dom'
  1. 明确好界面中的导航区、展示区
  2. 导航区的a标签改为Link标签
{/* 在React中靠路由链接实现切换组件--编写路由链接 */}
<Link to="/xxxxx">Demo</Link>
  1. 展示区写Route标签进行路径的匹配
	{/* 注册路由 */}
	<Route path='/xxxx' component={Demo}/>
  1. <App>的最外侧包裹了一个<BrowserRouter>或<HashRouter>

2. 路由组件与一般组件的区别

1.写法不同:
		一般组件:<Demo/>
		路由组件:<Route path="/demo" component={Demo}/>
2.存放位置不同:
		一般组件:components
		路由组件:pages
3.接收到的props不同:
		一般组件:写组件标签时传递了什么,就能收到什么
		 路由组件:接收到三个固定的属性
				history:
					go: ƒ go(n)
					goBack: ƒ goBack()
					goForward: ƒ goForward()
					push: ƒ push(path, state)
					replace: ƒ replace(path, state)
				location:
					pathname: "/about"
					search: ""
					state: undefined
				match:
					params: {}
					path: "/about"
					url: "/about"

3. BrowserRouter 和 HashRouter 的区别

BrowserRouter 和HashRouter 分别是路由器的两种形式,区别在于 BrowserRouter的路由器地址栏不带 # 号 ,而HashRouter的路由器地址栏带#号,
路由是需要路由器来管理的 ,所以路由链接和注册路由都要写在路由器里边 ,而且需要在同一个路由器里边,所以通常的做法是 在<App>的最外侧包裹了一个<BrowserRouter>或<HashRouter>

主要区别如下:
1.底层原理不一样:

  • BrowserRouter使用的是H5的history API,不兼容IE9及以下版本。
  • HashRouter使用的是URL的哈希值。

2.path表现形式不一样

  • BrowserRouter的路径中没有#,例如:localhost:3000/demo/test
  • HashRouter的路径包含#,例如:localhost:3000/#/demo/test

3.刷新后对路由state参数的影响

  • (1).BrowserRouter没有任何影响,因为state保存在history对象中
  • (2).HashRouter刷新后会导致路由state参数的丢失!!!
    备注:
  • HashRouter可以用于解决一些路径错误相关的问题。
index.jsx
ReactDOM.render(
	<BrowserRouter>
		<App/>
	</BrowserRouter>,
	document.getElementById('root')
)

4. Link 和NavLink

在这里插入图片描述

//<Link>和<NavLink>用来编写路由链接 实现前端切换组件
<Link className="list-group-item "to="home">home</Link>
<NavLink className="list-group-item "to="home">home</NavLink>
//现在有一个需求 就是在切换的路由链接上加高亮效果
 // 一般实在样式里边加 active就可以实现高亮的效果
 // 但是如果是加active 就不能用Link 而是要用 NavLink
<NavLink activeClassName="atguigu" className="list-group-item to="home">home</NavLink>
//  因为所有的组件信息最后都会渲染成HTML 所以可以直接在index.html里写样式信息
在index.html 里面
<style>
	.atguigu{
		background-color: rgb(209, 137, 4) !important;
		color: white !important;
	}
</style>

5. Switch

注册路由时,当有多个组件匹配相同的路径时,那么多个组件的内容都会展示在页面上。
比如说

<Link to="home">home</Link>

<Route path="home" component={Home}/>
<Route path="home" component={test1}/>
<Route path="home" component={test2}/>
// 如果这样写 那么home对应的路由组件都会展示在页面上

为了使得 路由匹配到路径后不再继续匹配 ,提高匹配的效率,引入<Switch >组件,<Switch>可以在匹配成功之后不再继续向下匹配

<Link to="home">home</Link>
<Switch>
<Route path="home" component={Home}/>
<Route path="home" component={test1}/>
<Route path="home" component={test2}/>
</Switch>

加了 <Switch>后在匹配到<Route path=“home” component={Home}/>后就不再匹配了 ,所以页面就显示Home组件的内容。

6. Redirect 重定向

1.一般写在所有路由注册的最下方,当所有路由都无法匹配时,跳转到Redirect指定的路由(当打开页面时默认跳转的路由页面)
2.具体编码:

<Switch>
		<Route path="/about" component={About}/>
		<Route path="/home" component={Home}/>
		<Redirect to="/about"/>
</Switch>

7. 嵌套路由

在这里插入图片描述
案列:
点击Home 路由链接, 路由跳转到Home 组件中的内容,Hone组件中同样拥有导航栏以及内容区。现在就是实现一个嵌套路由的功能
外部路由 :Home 和About
内部路由: Home 组件内的 News 和Message
内部路由组件 :News和 Message 这两个组件包含的是列表信息
index.jsx


//BrowserRouter 包裹App
ReactDOM.render(
	<BrowserRouter>
		<App/>
	</BrowserRouter>,
	document.getElementById('root')
)

App.jsx 编写外部路由

render() {
	return (
		<div>
				<div className="row">
					<div className="col-xs-offset-2 col-xs-8">
						React Router Demo
					</div>
		         </div>
		<div className="row">
					<div className="col-xs-2 col-xs-offset-2">
						<div className="list-group">
							<NavLink activeClassName="atguigu" className="list-group-item" to="/about">About</NavLink>
							<NavLink activeClassName="atguigu" className="list-group-item" to="/home">Home</NavLink>
						</div>
					</div>
					<div className="col-xs-6">
						<div className="panel">
							<div className="panel-body">
								{/* 注册路由 */}
								<Route path="/about" component={About}/>
								<Route path="/home" component={Home}/>
							</div>
						</div>
					</div>
		</div>
	</div>
		)
}

因为内部路由在Home组件中,所以在Home组件中编写内部路由

Home/index.jsx

render() {
	return (
		<div>
			<h3>我是Home的内容</h3>
			<div>
				<ul className="nav nav-tabs">
				  <li>
					 <NavLink activeClassName="atguigu" className="list-group-item "to="/home/news">News</NavLink>
				  </li>
					<li>
					<NavLink activeClassName="atguigu" className="list-group-item" to="/home/message">Message</NavLink>
					</li>
				</ul>
				{/* 注册路由 */}
				<Switch>
					<Route path="/home/news" component={News}/>
					<Route path="/home/message" component={Message}/>
					<Redirect to="/home/news"/>
				</Switch>
		  </div>
	  </div>
			)
	}

1.注册子路由时要写上父路由的path值,因为每次路由匹配时都是从父级路由开始匹配的 ,所以即便是三级路由,那么前两级的路由都要写上去。
注意: 如果有子级路由,一定不要在父级中开启精准匹配(exact),会导致丢失子路由信息
2.路由的匹配是按照注册路由的顺序进行的

8. 向路由组件传递参数数据

(1)路由组件接收params参数

在这里插入图片描述

案列:
当点击二级路由里面的Message 路由时 路由跳转到 Message组件中,不论点击列表 消息1 还是消息2 或者消息3 , 在下方展示详细信息里结构是一样的,包含: ID、 TILTLE 和CONTENT,唯一改变的是内容。
一级外部路由 :Home 和About
二级路由: Home 组件内的 News 和Message
二级路由组件 :News和 Message 这两个组件包含的是列表信息
三级路由:message组件内的信息列表
三级路由组件:点击消息1 展示详情 Detail组件

index.js
         这个和嵌套路由一样 由BrowserRouter 包裹 \<App>
App.jsx 编写外部路由 
          和嵌套路由一样不变
Home/index.jsx 
        二级路由不变

Message/index.jsx 三级路由

import React, { Component } from 'react'
import {Link,Route} from 'react-router-dom'
import Detail from './Detail'
export default class Message extends Component{
  state={
    meassageArr:[
    {id:'01',title:'消息1'}{id:'02',title:'消息2'}{id:'03',title:'消息3'}
    ]
  }
  render(){
   const {messageArr} = this.state
   return (
    <div>
      <ul>
         {
            messageArr.map(msgObj=>{
             {/* 路由链接 向路由组件传递params参数*/}
              <Link to={`/home/message/detail/${msgObj.id}/${msgObj.title}`}>{msgObj.title}</Link>
            })
         }
     </ul>
     {/* 注册路由 声明接收params参数*/}
     <Route path='home/message/detail/:id/:title' compoment={Detail}/>
    </div>
   )
  }
}

只要在注册路由中声明接收params参数,Detail就能收到 Id 和title 参数,给组件传递参数,无论是一般组件还是路由组件,接收参数都是用props
可以直接在Deatil 打印props信息
在这里插入图片描述
Deail/index.jsx

import React ,{Component}from 'react'
const DetailData = [
	{id:'01',content:'你好,中国'},
	{id:'02',content:'你好,尚硅谷'},
	{id:'03',content:'你好,未来的自己'}
]
export default class Detail extends Component {
	render() {
		console.log(this.props);
		// 接收params参数
		const {id,title} = this.props.match.params
		const findResult = DetailData.find((detailObj)=>{
			return detailObj.id === id
		})
		return (
			<ul>
				<li>ID:{id}</li>
				<li>TITLE:{title}</li>
				<li>CONTENT:{findResult.content}</li>
			</ul>
		)
	}
}

(2)路由组件接收search参数

还是上一个案列
在这里插入图片描述

页面 路由地址是不一样的
现在改变Message/index.jsx 组件

return (
    <div>
      <ul>
         {
            messageArr.map(msgObj=>{
             {/* 路由链接 向路由组件传递search参数*/}
              <Link to={`/home/message/detail/?id=${msgObj.id}&title=${msgObj.title}`}>{msgObj.title}</Link>
            })
         }
     </ul>
     {/* 注册路由 search 参数无须声明接收 正常注册路由即可*/}
     <Route path='home/message/detail' compoment={Detail}/>
    </div>
   )

Deail/index.jsx
在这里插入图片描述

import React, { Component } from 'react'
import qs from 'querystring'

const DetailData = [
	{id:'01',content:'你好,中国'},
	{id:'02',content:'你好,尚硅谷'},
	{id:'03',content:'你好,未来的自己'}
]
export default class Detail extends Component {
	render() {
		console.log(this.props);
		// 接收search参数
		const {search} = this.props.location
		const {id,title} = qs.parse(search.slice(1))

		const findResult = DetailData.find((detailObj)=>{
			return detailObj.id === id
		})
		return (
			<ul>
				<li>ID:{id}</li>
				<li>TITLE:{title}</li>
				<li>CONTENT:{findResult.content}</li>
			</ul>
		)
	}
}

import qs from 'querystring' //不用下载react自身有的
let obj = {name:'tom,age:18}
qs.stringify(obj)  // name=tom&age=18 urlencoded形式

let str = 'carName=奔驰&price=199'
qs.parse(str)  //{carName:"奔驰", price:"199"}
(3)路由组件接收state参数

在这里插入图片描述
还是上一个案列
页面 路由地址是不一样的
现在改变Message/index.jsx 组件

return (
    <div>
      <ul>
         {
            messageArr.map(msgObj=>{
            {/* 向路由组件传递state参数 */}
		  <Link to={{pathname:'/home/message/detail',state:{id:msgObj.id,title:msgObj.title}}}>{msgObj.title}</Link>
            })
         }
     </ul>
     {/* state参数无需声明接收,正常注册路由即可 */}
	<Route path="/home/message/detail" component={Detail}/>
    </div>
   )

Deail/index.jsx
在这里插入图片描述

import React, { Component } from 'react'
const DetailData = [
	{id:'01',content:'你好,中国'},
	{id:'02',content:'你好,尚硅谷'},
	{id:'03',content:'你好,未来的自己'}
]
export default class Detail extends Component {
	render() {
		console.log(this.props);

		// 接收state参数
		const {id,title} = this.props.location.state || {}

		const findResult = DetailData.find((detailObj)=>{
			return detailObj.id === id
		}) || {}
		return (
			<ul>
				<li>ID:{id}</li>
				<li>TITLE:{title}</li>
				<li>CONTENT:{findResult.content}</li>
			</ul>
		)
	}
}

(4)总结
1.params参数
       路由链接(携带参数)<Link to='/demo/test/tom/18'}>详情</Link>
       注册路由(声明接收)<Route path="/demo/test/:name/:age" component={Test}/>
       接收参数:this.props.match.params
2.search参数
	    路由链接(携带参数)<Link to='/demo/test?name=tom&age=18'}>详情</Link>
	    注册路由(无需声明,正常注册即可)<Route path="/demo/test" component={Test}/>
	    接收参数:this.props.location.search
	    备注:获取到的search是urlencoded编码字符串,需要借助querystring解析
3.state参数
		 路由链接(携带参数)<Link to={{pathname:'/demo/test',state:{name:'tom',age:18}}}>详情</Link>
		 注册路由(无需声明,正常注册即可)<Route path="/demo/test" component={Test}/>
		 收参数:this.props.location.state
		 备注:刷新也可以保留住参数

9. 路由的push 和replace模式

路由的工作原理就是对浏览器的历史记录进行操作 在路由跳转的过程中,有两种跳转方式:push模式和replace模式
push模式: 就是在路由一级一级跳转的过程中,可以一级一级的回退,就是压栈,回退的时候就是出栈,所以跳转的路由地址都存储在栈中
replace模式:也是压栈的方式,但是在跳转的时候 ,新的路由地址会覆盖上一级的路由。
默认是push模式
开启replace的模式的方式 : 在<Link >或者<NavLink> 标签中添加replace属性就可以开启replace模式

<Link replace to={{pathname:'/home/message/detail',state:{id:msgObj.id,title:msgObj.title}}}>{msgObj.title}</Link>

10. 不使用<Link >和<NavLink> 的路由跳转方式

通常情况下 我们实现路由的跳转是通过<Link >或者<NavLink> 的方式,如果有一个需求,是通过按钮的方式实现路由的跳转 ,该如何实现呢
案例:
不使用<Link >或者<NavLink> ,通过编写一段代码,让其实现路由跳转,且为replace跳转。

import {Button} from 'antd'
//还是上面的例子进行更改,因为路由需要传参数
	replaceShow = (id,title)=>{
		//replace跳转+携带params参数
		//this.props.history.replace(`/home/message/detail/${id}/${title}`)

		//replace跳转+携带search参数
		// this.props.history.replace(`/home/message/detail?id=${id}&title=${title}`)

		//replace跳转+携带state参数
		this.props.history.replace(`/home/message/detail`,{id,title})
	}

render() {
	const {messageArr} = this.state
	return (
		<div>
			<ul>
			  {
				 messageArr.map((msgObj)=>{
					 return (
					  <li key={msgObj.id}>
						   <Link to={{pathname:'/home/message/detail',state:{id:msgObj.id,title:msgObj.title}}}>{msgObj.title}</Link>
					      &nbsp;<Button onClick={()=>{this.repalceShow(msgObj.id,msgObj.title)}}>replace模式跳转</Button>
					  </li>
							)
						})
					}
				</ul>
				<hr/>
				<Route path="/home/message/detail" component={Detail}/>
			</div>
		)
	}

也可以通过这种方式实现push方式的跳转
this.props.history.push()
总结:

借助this.props.history对象上的API对操作路由跳转、前进、后退
	-this.props.history.push()
	-this.props.history.replace()
	-this.props.history.goBack()
	-this.props.history.goForward()
	-this.props.history.go()

11. withRouter 的使用

首先组件分为一般组件和路由组件

// 一般组件的引用方式
<Demo/>
// 路由组件的引用方式
<Route path="/XXX" component={Demo}/>

withRouter 的作用就是加工一般组件,使得一般组件拥有路由组件所特有的API
现在有一个需求,就是在组件中添加两个按钮 一个前进一个后退的按钮,实现路由的跳转

import React ,{Component} from 'react'
import {withRouter}from 'react-router-dom'
class Demo extends component{
  forwardShow=()=>{
   this.props.history.goForward()
 }
  backShow=()=>{
  this.props.history.goBack()
 }
   render(){
   return (
     <div>
        <h2>withRouter的使用</h2>
        <Button onClick={this.forwardShow}>前进</Button>
        <Button onClick={this.backShow} >后退</Button>
     </div>
   )
   }
}
export default withRouter(Demo)

withRouter函数接收一般组件,返回的新组件加上了路由组件所特有的API

参考:1. 尚硅谷视频
2.https://blog.csdn.net/xuxuii/article/details/125691593?utm_medium=distribute.pc_relevant.none-task-blog-2defaultbaidujs_baidulandingword~default-1-125691593-blog-119087451.pc_relevant_multi_platform_whitelistv4eslandingrelevant2&spm=1001.2101.3001.4242.2&utm_relevant_index=3

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值