【react框架】学习记录9-学习路由react-router-dom的使用

前言

附上汉化文档地址

学完发现6版成为新标准了,又有得记了,呜呜呜。

【react框架】学习记录12-React Router 6的学习使用记录

安装

yarn add react-router-dom

使用

相关组件

BrowserRouter

这个组件是history模式的路由器,用于管理包含在内的路由组件,所以一般会直接放在App.jsx外层,在index.js中写:

...
import {BrowserRouter} from 'react-router-dom'

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

没有这个其他路由组件就无法使用!

HashRouter

hash模式的路由器,用法和BrowserRouter一样。

Link与Route

这俩是配合使用的,Link的作用相当于a标签,改变路由。Route的作用就是显示对应路由的组件。例如:

...
import {Link,Route} from 'react-router-dom'
import Home from './components/Home'
import About from './components/About'

export default class App extends Component {
	render() {
		return (
			<div>
				<div>
					{/* 原生html中,靠<a>跳转不同的页面 */}
					{/* <a className="list-group-item" href="./about.html">About</a>
					<a className="list-group-item active" href="./home.html">Home</a> */}

					{/* 在React中靠路由链接实现切换组件--编写路由链接 */}
					<Link className="list-group-item" to="/about">About</Link>
					<Link className="list-group-item" to="/home">Home</Link>
				</div>
					
				<div>
					{/* 注册路由 */}
					<Route path="/about" component={About}/>
					<Route path="/home" component={Home}/>
				</div>
			</div>
		)
	}
}

其中<Route path="/about" component={About}/>这种把写好的组件放Route组件里的就叫做路由组件,这种组件默认的props内存在三个固定的属性:historylocationmatch

NavLink

就是个能绑定激活样式的Link标签:

<NavLink activeClassName="active" className="nav-link" to="./about">about</NavLink>
// 通过activeClassName绑定激活样式,不写这个属性默认获取找类名为active的样式。

但是如果每个标签都要这样写,当导航多了代码就会比较冗余,所以需要封装一个NavLink组件:

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

export default class MyNavLink extends Component {
	render() {
		return (
			<NavLink activeClassName="active" className="nav-link" {...this.props}/>
		)
	}
}

外部这样使用:

<MyNavLink to="/about">About</MyNavLink>

这里有个重点:其实在react必合标签中的内容,会被自动放入props中生成children属性中,所以上述例子中的About会被props接收,然后在MyNavLink组件内部中以属性的形式{...this.props}展开,自然而然的就能显示导航名称了。

Switch

这个组件是用来处理一个问题的:

<Link to="/about">About</Link>
<Link to="/home">Home</Link>
<Link to="/home">Home1</Link>

如果不同Link标签匹配同个路由,例如上面的/home,那么当路由切换至此的时候,对应路由组件都会显示。用Switch标签就能解决这个问题。

<Switch>
	<Link to="/about">About</Link>
	<Link to="/home">Home</Link>
	<Link to="/home">Home1</Link>
</Switch>

被包裹后,react从上到下匹配,当遇到第一个匹配/home的路由组件,就不继续往下匹配了。只会显示第一个/home的路由组件。

Redirect

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

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

exact模糊匹配和严格匹配

默认情况下,当<Link to="/about/a/b">About</Link>去访问<Route path="/about" component={About}/>是能匹配的上,渲染出来的,这就是模糊匹配。

当Route加上个exact属性<Route exact path="/about" component={About}/>或者<Route exact={true} path="/about" component={About}/>,就开启了严格匹配,上述情况就不会出现了。

Tip:

  1. react去做路由匹配其实拿的是/about/a/b里的about、a、b。
  2. 默认http://localhost:3000访问的时候,其实是http://localhost:3000/,只是地址栏把/隐藏了。所以匹配的从来不是符号,而是内容。

嵌套路由

例子中Link标签改用自封装的。

一级路由组件:

<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二级路由组件:

<MyNavLink to="/home/news">News</MyNavLink>
<MyNavLink to="/home/message">Message</MyNavLink>

{/* 注册路由 */}
<Switch>
	<Route path="/home/news" component={News}/>
	<Route path="/home/message" component={Message}/>
	<Redirect to="/home/news"/>
</Switch>

注意:

  1. 嵌套的子组件路由上一定要写父组件的路由,比如"/home/news"不能直接"/news",原理是首先父组件模糊匹配到home组件并展示,然后home中的子组件才能被渲染,接着在组件内去匹配,最后匹配到Message组件。
  2. 父组件不能开启exact精准匹配,否则直接匹配不上。

路由传参

params参数

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

{/* 声明接收params参数 */}
<Route path="/home/message/detail/:id/:title" component={ Zizujian }/>

路由子组件通过props的match属性获取params参数:

const { id, title } = this.props.match.params

search参数

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

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

路由子组件通过props的location属性获取search参数,但是内容是个urlencoded编码字符串,需要借助node的querystring解析。

import qs from 'querystring'
...
// 接收search参数
const { search } = this.props.location
const { id, title } = qs.parse(search.slice(1)) // 截取第一个?号后的内容

state参数

这个不是三大属性里的state:

{/* 向路由组件传递state参数 */}
<Link to={{pathname:'/home/message/detail', state:{ id: obj.id, title: obj.title }}}>{ obj.title }</Link>

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

路由子组件通过props的location属性获取state 参数

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

为什么后面要接个空对象呢?因为state参数是维护在浏览器的history对象中(可在this.props.location看到指向history的对象),如果强刷或者清除了浏览器缓存,那么history的对象就被重置了,state参数就会被丢失(平时刷新没事)。

这种传参的方式好处就是url地址看不见参数,有一定的隐蔽性。

replace模式

可以在link标签里通过replace属性,开启replace模式:

<Link replace to="/about">About</Link>

编程式导航

提供编程式导航方法的对象在props里的history中,方法有:

<button onClick={()=> this.pushShow(obj.id, obj.title)}>push查看</button>
<button onClick={()=> this.replaceShow(obj.id, obj.title)}>replace查看</button>
<button onClick={this.back}>回退</button>
<button onClick={this.forward}>前进</button>
<button onClick={this.go}>go</button>

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) // -代表后退,数字代表几步
}

withRouter加工

一般组件中的props是没有路由组件的相关路由对象的,所以一般组件内部不能使用编程式导航,需要通过withRouter这个api将一般组件添加上路由对象:

import {withRouter} from 'react-router-dom' // 第一步
...
export default withRouter(Header) // 把一般组件的类通过withRouter的处理导出,即可在这个组件中使用编程式导航

懒加载

和vue的懒加载一样的原理

import React, { Component, lazy, Suspense } from 'react'

import About from './Loading' // 因为网络请求懒加载组件有个时间差,所以在这段过程中要默认显示一个组件,必须非按需引入
const Home = lazy(()=>import('./Home'))
const About = lazy(()=>import('./About'))

<Suspense fallback={<Loading/>}> // 这个组件就是当正在懒加载的时候,默认显示的组件,且是必须的
    <Route path="/about" component={About}/>
	<Route path="/home" component={Home}/>
</Suspense>

路由问题

多级路由刷新页面样式丢失

前置知识:其实http://localhost:3000在框架内部就代表工程目录下的public文件夹,然后默认读取public/index.html,所以我们其实还可以通过http://localhost:3000/favicon.ico访问图标文件,如果去访问不存在的地址,那么会默认访问index.html文件,但显示的url不变。

场景:当页面url地址为一个多级嵌套的路由时,我们去刷新页面会出现,index.html文件中link引入的公共css文件丢失的情况,导致页面样式出现问题。

原因:link引入css的路径是相对地址,例如'./css/style.css',当刷新页面的时候,会误以为一级路由是文件地址,所以会以http://localhost:3000/一级路由作为基本地址,以这个地址的相对路径去找css文件http://localhost:3000/一级路由/css/style.css,能找到才怪。

解决

  1. 写成绝对地址'/css/style.css',推荐。
  2. 写react独有的绝对地址'%PUBLIC_URL%/css/style.css'
  3. 使用哈希模式<HashRouter>

BrowserRouter与HashRouter的区别

底层原理不一样:

  1. BrowserRouter使用的是H5的history API二次封装对象,不兼容IE9及以下版本。
  2. HashRouter使用的是URL的哈希值。

path表现形式不一样:

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

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

  1. BrowserRouter没有任何影响,因为state保存在history对象中。
  2. HashRouter刷新后会导致路由state参数的丢失!!!因为hash模式不用history对象。

其他:HashRouter可以用于解决一些路径错误相关的问题,比如之前提到的样式丢失。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
React Router是一个用于建立SPA应用程序的库,它提供了一种将应用程序状态与URL同步的方式。React Router v6是React Router的最新版本,它引入了一些新的概念和语法。 下面是使用React Router v6搭建路由的步骤: 1. 安装React Router v6 使用npm或者yarn安装React Router v6: ``` npm install react-router-dom@next ``` 2. 创建路由 在应用程序的入口文件中,使用BrowserRouter创建一个路由: ```jsx import { BrowserRouter } from 'react-router-dom'; ReactDOM.render( <BrowserRouter> <App /> </BrowserRouter>, document.getElementById('root') ); ``` 3. 定义路由 使用Route组件定义路由。Route组件的path属性指定URL的路径,component属性指定该路径对应的组件: ```jsx import { Route } from 'react-router-dom'; function App() { return ( <div> <Route path="/" component={Home} /> <Route path="/about" component={About} /> </div> ); } ``` 在上面的例子中,当URL路径为“/”时,渲染Home组件;当URL路径为“/about”时,渲染About组件。 4. 处理404页面 使用Route组件的exact属性确保路由匹配完全相等的路径。这样可以避免在匹配“/”路径时同时匹配到所有其他路径。 使用Route组件的path属性指定“*”路径,即匹配所有路径的路径。将这个Route组件放在所有其他Route组件的下面,就可以为未匹配到任何路径的URL显示404页面: ```jsx import { Route, Routes } from 'react-router-dom'; function App() { return ( <div> <Routes> <Route path="/" element={<Home />} /> <Route path="/about" element={<About />} /> <Route path="*" element={<NotFound />} /> </Routes> </div> ); } ``` 在上面的例子中,当URL路径为任何未匹配到的路径时,渲染NotFound组件。 5. 嵌套路由 使用Routes组件嵌套Route组件,可以创建嵌套路由。在嵌套路由中,父级Route组件的path属性包含所有子级Route组件的path属性的前缀。 ```jsx import { Route, Routes } from 'react-router-dom'; function App() { return ( <div> <Routes> <Route path="/" element={<Home />} /> <Route path="/about" element={<About />}> <Route path="team" element={<Team />} /> <Route path="history" element={<History />} /> </Route> <Route path="*" element={<NotFound />} /> </Routes> </div> ); } ``` 在上面的例子中,当URL路径为“/about/team”时,渲染Team组件;当URL路径为“/about/history”时,渲染History组件。 React Router v6引入了一些新的语法,例如使用Routes组件代替Router组件,使用element属性代替component属性等。这些语法的目的是让React Router更加简洁易用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值