React系统学习笔记--超基础--超详细--超简洁--React路由(五)

1 相关理解

1.1 SPA的理解

单页Web应用(single page web application,SPA)

整个应用只有一个完整的页面

点击页面中的链接不会刷新页面,只会做页面的局部更新

数据都需要通过ajax请求获取,并在前端异步展现

1.2 路由的理解

什么是路由

一个路由就是一个映射关系(key:value)

key为路径, value可能是function或component

路由分类

后端路由

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

前端路由

  • 浏览器端路由,value是component,用于展示页面内容
  • 注册路由: <Route path="/test" component={Test}>
  • 工作过程:当浏览器的path变为/test时, 当前路由组件就会变为Test组件

1.3 react-router-dom的理解

相关概念

  • react的一个插件库

  • 专门用来实现一个SPA应用

  • 基于react的项目基本都会用到此库

相关api

内置组件

  • <BrowserRouter>
  • <HashRouter>
  • <Route>
  • <Redirect>
  • <Link>
  • <NavLink>
  • <Switch>

其他

  • history对象

  • match对象

  • withRouter函数

2 路由的基本使用

1、 明确好界面中的导航区、展示区

2、 导航区的a标签改为Link标签

<Link to="/xxxxx">Demo</Link>

3、 展示区写Route标签进行路径的匹配

<Route path='/xxxx' component={
    
    Demo}/>

4、 <App>的最外侧包裹了一个<BrowserRouter>或<HashRouter>

//路由编写和路由注册都靠一个BrowserRouter去管理,所以BrowserRouter写外侧,一直扩
//还有一种就是BrowserRouter写在入口文件中,包住App就可以
ReactDOM.render(
	<BrowserRouter><App/></BrowserRouter>,
	document.getElementById('root')
)

细节:Link最终在浏览器转换为a标签

3 路由组件与一般组件

写法不同

一般组件:<Demo/>

路由组件:<Route path="/demo" component={Demo}/>

存放位置不同

一般组件:components

路由组件:pages

接收到的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"

Link标签,不能追加一个高亮样式,用Link的升级版:

4 NavLink使用与封装

NavLink可以实现路由链接的高亮,通过activeClassName指定样式名

默认高亮就是active,正好与bootstrap样式类名相同。我们也可以自己指定样式(权重给高一点盖过bootstrap)

.NavTitle {
    
    
    background-color: rgb(183, 238, 119) !important;
    color: #fff !important;
}
<NavLink activeClassName='NavTitle' className="list-group-item" to='/about'>About</NavLink>
<NavLink activeClassName='NavTitle' className="list-group-item" to='/home'>Home</NavLink>

要是按上面这样写,感觉重复内容有点多,于是可以封装一下:

封装MyNavLink(一般组件)

props用于传递标签属性,标签体内容怎么接收呢!

重点:标签体内容其实也是特殊的属性,只不过我们只能写value,key是人家指定的children;所以我们其实也可以不写标签体内容,然后通过children标签属性来配置

 //封装示例
 export default class MyNavLink extends Component {
    
    
 	render() {
    
    
 		return (
               /* 注意:这里也把标签体内容带过去了,在children属性里面 */
 			<NavLink activeClassName="NavTitle" className="list-group-item" {
    
    ...this.props}/>
 		)
 	}
 }
 //使用--标签体内容直接写,props会帮我们收集到children标签属性里面
 <MyNavLink to="/about">About</MyNavLink>
 <MyNavLink to="/home">Home</MyNavLink>

5 Switch的使用

通常情况下,path和component是一一对应的关系

但是如果出现两个相同的路径,匹配两个不同的组件,我们想让它匹配到第一个之后就结束了(提高效率)

Switch可以提高路由匹配效率(单一匹配) ---- 即匹配到一个后将不再往下匹配

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

6 解决多级路径刷新页面样式丢失的问题

为什么会丢失

写多级路由的时候,刷新页面之后会丢失,浏览器会把前面一级的路由当作public文件夹下的,错误路径返回的结果由index.html兜底

三种解决方法

public/index.html 中 引入样式时不写 ./ 写 / (常用)

public/index.html 中 引入样式时不写 ./ 写 %PUBLIC_URL% (常用,但只在react中有效果)

使用HashRouter (不常用),#后面的是前端资源,不带给服务器

<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8" />
		<title>react脚手架</title>
   <!-- 方法二 -->
		<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
     <!-- 方法一 -->
		<link rel="stylesheet" href="/css/bootstrap.css">
	</head>
	<body>
		<div id="root"></div>
	</body>
</html>

7 路由的严格匹配与模糊匹配

给的路径是/home,人家要的是/home/a/b,不匹配

给的路径是/home/a/b,人家要的是/home,匹配—模糊匹配

给的路径是/a/home/b,人家要的是/home,不匹配

模糊匹配

默认使用的是模糊匹配(简单记:【输入的路径】必须包含要【匹配的路径】多了可以不能少,且顺序要一致)

严格匹配

开启严格匹配:<Route exact={true}path=“/about” component={About}/> ,可以省略exact={true}exact

注意:严格匹配不要随便开启,需要再开,有些时候开启会导致无法继续匹配二级路由

//编写路由链接
	<MyNavLink to="/about">About</MyNavLink>
	<MyNavLink to="/home/a/b">Home</MyNavLink>

{
    
    /* 注册路由 */}
	<Switch>
		<Route exact path="/about" component={
    
    About}/>
		<Route exact path="/home" component={
    
    Home}/>
	</Switch>

8 Redirect的使用

想要页面上来就帮我选中一个

一般写在所有路由注册的最下方,当所有路由都无法匹配时,跳转到Redirect指定的路由

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

9 嵌套路由

注册子路由时要写上父路由的path值

路由的匹配是按照注册路由的顺序进行的

-------------------注册一级路由-----------------------------
	{
    
    /* 在React中靠路由链接实现切换组件--编写路由链接 */}
	<MyNavLink to="/about">About</MyNavLink>
	<MyNavLink to="/home">Home</MyNavLink>
   {
    
    /* 注册路由 */}
	<Switch>
		<Route path="/about" component={
    
    About}/>
		<Route path="/home" component={
    
    Home}/>
		<Redirect to="/about"/>
	</Switch>
----------------------注册二级路由 :Home组件-----------------------------------
   <div>
		<ul className="nav nav-tabs">
			<li>
				<MyNavLink to="/home/news">News</MyNavLink>
			</li>
			<li>
				<MyNavLink to="/home/message">Message</MyNavLink>
			</li>
		</ul>
		{
    
    /* 注册路由 */}
		<Switch>
			<Route path="/home/news" component={
    
    News}/>
			<Route path="/home/message" component={
    
    Message}/>
			<Redirect to="/home/news"/>
		</Switch>
	</div>

10 向路由组件传递参数

10.1 params参数

路由链接(携带参数):<Link to='/demo/test/tom/18'}>详情</Link>

注册路由(声明接收):<Route path="/demo/test/:name/:age" component={Test}/>

接收的参数存在参数:this.props.match.params

	-------------------------------发送参数:父组件----------------------------------------------
	<div>
       {
    
    /* 向路由组件传递params参数 */}
       <Link to={
    
    `/home/message/detail/${
      
      msgObj.id}/${
      
      msgObj.title}`}>{
    
    msgObj.title}</Link>
       <hr />
       {
    
    /* 声明接收params参数 */}
       <Route path="/home/messages/detail/:id/:title" component={
    
    Detail} />
  </div>
	--------------------------------接受参数:子组件-----------------------------------------------------------
    const {
    
    id,title} = this.props.match.params

10.2 search参数

路由链接(携带参数):<Link to='/demo/test?name=tom&age=18'}>详情</Link>

注册路由(无需声明,正常注册即可):<Route path="/demo/test" component={Test}/>

接收参数:this.props.location.search(没帮我们整理成对象形式)

备注:获取到的search是urlencoded编码字符串,需要借助qs库解析

注意:由于search接收的时候没帮我们处理成对象形式,所以我们需要借助react中的库来处理。新版的库与之前的不同import qs from 'qs'

name=tom&age=18这种编码形式叫做urlencoded编码形式

对象转urlencoded:qs.stringify(obj)

urlencoded转对象:qs.parse(str)

	-------------------------------发送参数:父组件----------------------------------------------
	<div>
      	{
    
    /* 向路由组件传递search参数 */}
		<Link to={
    
    `/home/message/detail/?id=${
      
      msgObj.id}&title=${
      
      msgObj.title}`}>{
    
    msgObj.title}</Link>
       <hr />
     	{
    
    /* search参数无需声明接收,正常注册路由即可 */}
		<Route path="/home/messages/detail" component={
    
    Detail}/>
  </div>
	--------------------------------接受参数:子组件-----------------------------------------------------------
    import qs from 'qs'
	// 接收search参数
	const {
    
    search} = this.props.location
	const {
    
    id,title} = qs.parse(search.slice(1))

10.3 state参数

地址栏是看不见参数的,但是刷新数据不会丢,因为我们目前使用的是BrowserRouter,一直操作浏览器的history(location是history中的一个属性,不会丢;清空历史记录的话就会丢)

路由链接(携带参数):<Link to={ {pathname:'/demo/test',state:{name:'tom',age:18}}}>详情</Link>,to写成对象

注册路由(无需声明,正常注册即可):<Route path="/demo/test" component={Test}/>

接收参数:this.props.location.state

  • 备注:使用BrowserRouter刷新才可以保留住参数,使用HashRouter刷新后state将会没有history来保存参数
  • 子组件接受参数时const {id,title} = this.props.location.state || {} ,后面添加||{}是防止使用HashRouter后state为undefined时报错
	-------------------------------发送参数:父组件----------------------------------------------
	<div>
      	{
    
    /* 向路由组件传递state参数 */}
		<Link to={
    
    {
    
    pathname:'/home/messages/detail',state:{
    
    id:msgObj.id,title:msgObj.title}}}>{
    
    msgObj.title}</Link>

       <hr />
     	{
    
    /* state参数无需声明接收,正常注册路由即可 */}
		<Route path="/home/message/detail" component={
    
    Detail}/>
  </div>
	--------------------------------接受参数:子组件-----------------------------------------------------------
    // 接收state参数,后面添加`||{}`是防止使用`HashRouter`后state为undefined时报错
	const {
    
    id,title} = this.props.location.state || {
    
    }

11 push与replace

push是压栈操作,会留下历史记录。replace是替换操作,把栈顶元素替换成当前元素。

push模式:默认开启push模式

replace模式:replace={true},可以简写为 replace

<Link replace={
    
    true} to={
    
    {
    
     pathname: '/home/messages/detail', state: {
    
     id: msgObj.id, title: msgObj.title } }}>{
    
    msgObj.title}</Link>&nbsp;&nbsp;

12 编程式路由导航

有一个需求:等3秒钟自动路由跳转,借助Link和NavLink难以实现

借助this.prosp.history对象上的API对操作路由跳转、前进、后退

this.prosp.history.push()

将历史记录压入栈

this.props.history.replace()

替代栈位置,即不会产生历史记录

this.props.history.goBack()

回退一格

this.props.history.goForward()

前进一格

this.props.history.go()

前进或者后退n格(根据传入的数字正负数)

import React, {
    
     Component } from 'react'
import {
    
     Link, Route } from 'react-router-dom'
import Detail from './Detail'

export default class Message extends Component {
    
    
 state = {
    
    
   messageArr: [
     {
    
     id: '01', title: '消息1' },
     {
    
     id: '02', title: '消息2' },
     {
    
     id: '03', title: '消息3' },
   ]
 }

 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 })
 }

 pushShow = (id, title) => {
    
    
   //push跳转+携带params参数
   // this.props.history.push(`/home/message/detail/${id}/${title}`)

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

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

 }

 back = () => {
    
    
   this.props.history.goBack()
 }

 forward = () => {
    
    
   this.props.history.goForward()
 }

 go = () => {
    
    
   this.props.history.go(-2)
 }

 render() {
    
    
   const {
    
     messageArr } = this.state
   return (
     <div>
       <ul>
         {
    
    
           messageArr.map((msgObj) => {
    
    
             return (
               <li key={
    
    msgObj.id}>

                 {
    
    /* 向路由组件传递params参数 */}
                 {
    
    /* <Link to={`/home/message/detail/${msgObj.id}/${msgObj.title}`}>{msgObj.title}</Link> */}

                 {
    
    /* 向路由组件传递search参数 */}
                 {
    
    /* <Link to={`/home/message/detail/?id=${msgObj.id}&title=${msgObj.title}`}>{msgObj.title}</Link> */}

                 {
    
    /* 向路由组件传递state参数 */}
                 <Link to={
    
    {
    
     pathname: '/home/message/detail', state: {
    
     id: msgObj.id, title: msgObj.title } }}>{
    
    msgObj.title}</Link>
			&nbsp;<button onClick={
    
    () => this.pushShow(msgObj.id, msgObj.title)}>push查看</button>
			&nbsp;<button onClick={
    
    () => this.replaceShow(msgObj.id, msgObj.title)}>replace查看</button>
               </li>
             )
           })
         }
       </ul>
       <hr />
       {
    
    /* 声明接收params参数 */}
       {
    
    /* <Route path="/home/message/detail/:id/:title" component={Detail}/> */}

       {
    
    /* search参数无需声明接收,正常注册路由即可 */}
       {
    
    /* <Route path="/home/message/detail" component={Detail}/> */}

       {
    
    /* state参数无需声明接收,正常注册路由即可 */}
       <Route path="/home/message/detail" component={
    
    Detail} />

       <button onClick={
    
    this.back}>回退</button>&nbsp;
       <button onClick={
    
    this.forward}>前进</button>&nbsp;
       <button onClick={
    
    this.go}>go</button>
     </div>
   )
 }
}
//记得detail组件中也要响应修改接收的参数形式
//news组件中用编程式路由导航控制路由跳转
componentDidMount() {
    
    
    setTimeout(() => {
    
    
        this.props.history.push('/home/messages')
    }, 2000)
}

13 withRouter的使用

withRouter可以加工一般组件,让一般组件具备路由组件所特有的API

withRouter的返回值是一个新组件

import React, {
    
     Component } from 'react'
import {
    
     withRouter } from 'react-router-dom'
class Header extends Component {
    
    
 back = () => {
    
     this.props.history.goBack()}
 forward = () => {
    
    this.props.history.goForward()}
 go = () => {
    
     this.props.history.go(-2)}
 render() {
    
    
   console.log('Header组件收到的props是', this.props);
   return (
     <div className="page-header">
       <h2>React Router Demo</h2>
       <button onClick={
    
    this.back}>回退</button>&nbsp;
       <button onClick={
    
    this.forward}>前进</button>&nbsp;
       <button onClick={
    
    this.go}>go</button>
     </div>
   )
 }
}
export default withRouter(Header)

14 BrowserRouter与HashRouter的区别

前端路由的原理就是:点击路由链接引起路径的变化,被路由器检测到,多次点击留下历史记录

备注:HashRouter可以用于解决一些路径错误相关的问题。即在问题6中引入文件时可以不进行路径修改

14.1 底层原理不一样

BrowserRouter使用的是H5的history API,不兼容IE9及以下版本

但一般来说都用的这个

HashRouter使用的是URL的哈希值

14.2 path表现形式不一样

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

HashRouter的路径包含#,例如:localhost:3000/#/demo/test

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

BrowserRouter没有任何影响,因为state保存在history对象中

HashRouter刷新后会导致路由state参数的丢失!!!,因为HashRouter不用history这个API

猜你喜欢

转载自blog.csdn.net/m0_55644132/article/details/127831323
今日推荐