React学习记录 ---第五章

前四章在这里:React学习记录 ---第一章-CSDN博客

React学习记录 ---第二章-CSDN博客

React学习记录---第三章-CSDN博客

React学习记录 ---第四章-CSDN博客

React路由

前置知识

SPA

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

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

        之前是靠切换页面实现路由切换,比如:http://127.0.0.1/index.html,http://127.0.0.1/home.html,这是多页面应用;

{/* 原生html中,靠<a>跳转不同的页面 */}
<a className="list-group-item" href="./about.html">About</a>
<a className="list-group-item" href="./home.html">About</a>

        而单页面应用是切换组件实现路由切换单页面多组件,比如上来是http://127.0.0.1,我点击Home,路由会变成http://127.0.0.1/home,每个路径对应的是一个组件

import { Link, BrowserRouter } from 'react-router-dom'
{/* 在React中靠路由连接实现切换组件 */}
<BrowserRouter>
    <Link className="list-group-item" to="/about">About</Link>
    <Link className="list-group-item" to="/home">Home</Link>
    .
    .
    .
    {/* 注册路由 */}
	<Route path="/about" component={About}/>
    <Route path="/home" component={Home}/>
</BrowserRouter>

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

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

路由

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

          key-----value

          path-----component / function

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

        举例如下:

app.get("/search/users",function(req,res){
    const {q} = req.query
    axios({
        url:'http://127.0.0.1',
        params:{q}
    }).then(response =>{
        res.json(response.data)
    })
})

2.前端路由:

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

方式一,直接使用H5推出的history身上的API

let history = History.createBrowserHistory()

方式二,hash值(锚点),路由带#,兼容性比较好

let history = History.createHashHistory()
history.push(path)
history.replace(path)
history.goBack(path)
history.forward(path)

浏览器的历史记录是一个栈的结构

history.push  用于在浏览器的历史堆栈中添加一个新的条目,可以改变浏览器的 URL 并导航到一个新路径,同时不会重新加载页面;有痕

history.replace  不是压入栈顶,而是将栈顶路由替换;无痕

history.forward  是用来前进到浏览器历史记录中的下一个页面,就像用户点击了浏览器的前进按钮一样

react-router-dom

        react-router库有三种实现,分别给三种场景使用,分别是web、native和any,这里我们学习web场景的库:react-router-dom

        react的一个插件库,专门用来实现一个SPA应用,基于react的项目基本都会用到此库

react-router-dom相关API(@5)

内置组件--7个

Router(路由器)分两种:BrowserRouter和HashRouter(带#)

Route(路由)

路由的基本使用----+4
  1. <BrowserRouter>
  2. <HashRouter>
  3. <Route>
  4. <Link>

 index.js

import React from 'react'
import ReactDOM from 'react-dom'
import {BrowserRouter} from 'react-router-dom'
import App from './App'
ReactDOM.render(
	<BrowserRouter>
		<App/>
	</BrowserRouter>,
	document.getElementById('root')
)

App.jsx

import React, { Component } from 'react'
import {Link,Route} from 'react-router-dom'
import Home from './components/Home'
import About from './components/About'
<div className="row">
	<div className="col-xs-2 col-xs-offset-2">
		<div className="list-group">
			{/* 在React中靠路由链接实现切换组件--编写路由链接 */}
			<Link className="list-group-item" to="/about">About</Link>
			<Link className="list-group-item" to="/home">Home</Link>
		</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>

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

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

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

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

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

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

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

        1.写法不同:

                一般组件:<Demo />

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

        2.存放位置不同:

                一般组件:components

                路由组件:pages

        3.接收到的props不同:

                一般组件:写组件标签时传递了什么,就能收到什么

                路由组件:接收路由器传递给的三个固定的属性:history,location,match            

                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"

<NavLink>---+1

         NavLink替换的是Link的位置,NavLink可以实现路由链接的高亮,通过activeClassName指定样式名。   

封装 NavLink 组件

//未封装
<NavLink activeClassName="atguigu" className="list-group-item" to="/about">About</NavLink>
//封装后
<MyNavLink to="/about">About</MyNavLink>

其中About是通过this.props上的children属性带过去的,标签体内容是一个特殊的标签属性

components/MyNavLink/index.jsx

import React, { Component } from 'react'
import {NavLink} from 'react-router-dom'

export default class MyNavLink extends Component {
	render() {
		return (
			<NavLink activeClassName="atguigu" className="list-group-item" {...this.props}/>
		)
	}
}
<Switch>---+1

        通常情况下,path和component是一一对应的关系,一对多会导致效率低下

        Switch可以提高路由匹配效率(单一匹配)

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

 如上被包裹之后,就不会进行重复匹配了

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

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

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

        3.使用HashRouter(少见)

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

1.默认使用的是模糊匹配(简单记:【输入的路径】必须包含要【匹配的路径】,且顺序要一致)

比如/home---/home/a/b可以,  ---/a/home/b不对,顺序不对

2.开启严格匹配:<Route exact={true} path="/about" component={About}/>

或者简写为<Route exact path="/about" component={About}/>

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

<Redirect>---+1

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

2.具体编码:

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

    <Redirect to="/about"/>
</Switch>

嵌套路由

        1.注册子路由时要写上父路由的path值   path=“/home/news”, path=“/home/message”,

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

        如果给/home开启严格模式,他后面的子路由就全废了

向路由组件传递参数

1.params参数

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

                      或<Link to={` /demo/test/${name}/${age} `}>详情</Link>

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

        组件内接收参数:this.props.match.params

使用场景最多

2.search参数

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

                或<Link to='/demo/test?name=${name}&age=${age}'}>详情</Link>

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

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

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

import qs from 'querystring'

let obj = {name: 'tom', age: 18} 
qs.stringify(obj)  //name=tom & age=18

let str = 'carName=奔驰&price=99'
qs.parse(str) //{carName: '奔驰', price: 99} 

 提取search参数,不能直接使用,需要截取掉问号+转成对象,才能使用!

//接受search参数
const {search} = this.props.match.params
const {id,title} = qs.parse(search.slice(1))//去掉?

 使用场景较多

3.state参数

        路由链接(携带参数):<Link to={{pathname:'/demo/test',state:{id:'01',title:'消息'}}}>详情</Link>

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

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

        备注:刷新也可以保留住参数

由于我们使用的是BrowserRouter,他身上的history对象帮我们记着呢

应用场景:想要携带一些参数过去,但又不想让用户知道,比如一个人的手机号

编程式路由导航

       编程式路由导航和正常写路由链接没啥区别,其实玩得就是history身上的API

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

        -this.prosp.history.push()

        -this.prosp.history.replace()

        -this.prosp.history.goBack()

        -this.prosp.history.goForward()

        -this.prosp.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>
		)
	}
}

 withRouter

如何让一般组件也能用上路由组件的API呢?

(ps:vue不分一般组件和路由组件,其实例对象上都有这些方法)

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)

 加工前:

加工后:

BrowserRouter与HashRouter的区别

1.底层原理不一样:

        BrowserRouter底层调用的是H5的history API,this.props.history是react对他的二次封装,不兼容IE9及以下版本。

        HashRouter使用的是URL的哈希值。/#/a/b/c,#后面的部分不会发给服务器

        例如,如果你的URL是这样的:

        http://www.example.com/#/home

        这里的 #/home 是哈希部分,它告诉 HashRouter 应该渲染哪个组件(在这个例子中是主页)。但是,当这个请求发送到服务器时,服务器实际上只接收到:

        http://www.example.com/

        服务器不会看到 #/home 部分,因此也不会处理它

        总结:1如果你需要服务器能够访问路由参数,你可能需要使用其他方法,比如 BrowserRouter,它使用HTML5的history API来实现路由,并且可以向服务器发送完整的URL路径。但是,使用 BrowserRouter 时,服务器必须配置以正确处理不同的路径        

        2、尽管HashRouter的兼容性比BrowserRouter的兼容性好,但一般使用react开发,浏览器不会低于IE9。

2.path表现形式不一样

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

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

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

        (1).BrowserRouter没有任何影响,因为state保存在history对象中。

        (2).HashRouter刷新后会导致路由state参数的丢失!!!

4.备注:HashRouter可以用于解决一些路径错误相关的问题,比如上面提到过的样式丢失

关注我,不迷路!

会不定时为大家更新优质内容!收藏起来方便随时查看!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值