现代的前端应用大多都是SPA(单页应用程序),也就是只有一个HTML页面的应用程序。因为它的用户体验更好、对服务器的压力更小,所以更受欢迎。为了有效的使用单个页面来管理原来多页面的功能,前端路由应运而生
前端路由的功能:让用户从一个视图(页面)导航到另一个视图(页面),前端路由是一套映射规则,在Reat中是URL路径与组件的对应关系,使用 React路由简单来说,就是配置路径和组件(配对)。
文档:https://react-router.docschina.org/web/guides/philosophy
npm i react-router-dom
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
import React from 'react'
import ReactDom from 'react-dom'
// 导入路由组件
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
const App = () => (
// 使用Router组件包裹整个应用
<Router>
<div>
<h1>react</h1>
{/* 指定路由入口 */}
<Link to="/first">页面1</Link>
<Link to="/second">页面2</Link>
{/* 指定路由出口,path设置为Link中的to属性,component设置为要渲染的组件 */}
<Route path="/first" component={First}></Route>
<Route path="/second" component={Second}></Route>
</div>
</Router>
)
class First extends React.Component {
render() {
return (
<div>
<h2>我是页面1的标题</h2>
</div>
)
}
}
const Second = () => (
<div>
<h2>我是页面2</h2>
</div>
)
ReactDom.render(<App />, document.getElementById('root'));
import { BrowserRouter, Route, Link } from "react-router-dom";
import { HashRouter, Route, Link } from "react-router-dom";
hash模式下#后边的路径不会发给服务器,不会被包括在 HTTP 请求中,对后端完全没有影响,因此改变 hash 不会重新加载页面,在处理相对路径方面的一些问题时,使用HashRouter可以解决。
Link组件:用于指定导航链接,默认会被渲染为一个a标签,Link组件的to属性会作为href值
<Link to="/first">页面1</Link>
<a href="/first">页面1</a>
Route组件:指定路由展示的组件(注册路由)
<Route path="/first" component={First}></Route>
如果没有path属性,将匹配所有的路径。
NavLink可以实现路由链接的高亮,通过activeClassName指定样式名。当点击哪个导航链接,哪个导航菜单就会应用activeClassName指定的样式。
对NavLink再做一层封装
import React, { Component } from 'react'
import { NavLink } from 'react-router-dom'
export default class MyNavLink extends Component {
render() {
console.log(this.props)
return <NavLink activeClassName="active" {...this.props}></NavLink>
}
}
使用
<MyNavLink to="/home" children="home" />
<MyNavLink to="/about" children="about" />
默认情况下,在匹配到一个路由后会继续往下匹配。比如下方代码,在/home路径匹配到Home组件的情况下,依然会继续往下匹配到Test组件
但是一个路由一般只对应一个组件,在已经匹配到的情况下就没有必要继续往下匹配了。此时可以使用Switch组件,Switch可以提高路由匹配效率(单一匹配)。
import { Route, Switch } from 'react-router-dom'
注意!!!在 react-router-dom的6.x
版本中,“Switch”被替换为了“Routes”,需要更新导入语句
import { Switch, Route } from "react-router-dom";
// 更新为
import { Routes ,Route } from 'react-router-dom';
同时需要更新Route的声明语句
<Route path="/" component={Home} />
// 更新为
<Route path='/welcome' element={> } />
一般写在所有路由注册的最下方, 当所有路由都无法匹配时,跳转到Redirect指定的路由
<Route path="/about" component={About} />
<Route path="/home" component={Home} />
<Redirect to="/home">Redirect>
完全匹配 from;相当于 Route.exact。
严格匹配 from;相当于 Route.strict。
一般组件:
路由组件:
一般组件:components
路由组件:pages
一般组件:写组件标签时传递了什么,就能收到什么
路由组件:接收到三个固定的属性
为什么是从props上拿到history对象呢?我们创建的组件是没有history对象的,在Route组件中渲染了自己创建的组件,然后通过prop传了history进去。组件就可以通过props拿到history
import React from 'react'
import ReactDom from 'react-dom'
// 导入路由组件
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
// import { HashRouter as Router, Route, Link } from "react-router-dom";
class App extends React.Component {
render() {
return (
<Router>
<div>
<p>编程式导航</p>
<Link to="/login">去登录页面</Link>
<Route path="/login" component={Login}></Route>
<Route path="/home" component={Home}></Route>
</div>
</Router>
)
}
}
class Login extends React.Component {
handleLogin = () => {
this.props.history.push("/home");
}
render() {
return (
<div>
<h1>登录界面</h1>
<button onClick={this.handleLogin}>首页</button>
</div>
)
}
}
const Home = (props) => {
const goBack = () => {
props.history.go(-1);
}
return (
<div>
<h2>首页</h2>
<button onClick={goBack}>返回</button>
</div>
)
}
ReactDom.render(<App />, document.getElementById('root'));
一般组件的props上的history是undefined,无法使用编程式导航的api。此时可以用withRouter函数。
import React, { Component } from 'react'
import { withRouter } from 'react-router-dom'
// Header是一般组件
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="head">
<h1>React-router-dom</h1>
<button onClick={this.back}>回退</button>
<button onClick={this.forward}>前进</button>
<button onClick={this.go}>go</button>
</div>
)
}
}
// withRouter是一个函数,可以加工一般组件,让一般组件具备路由组件所特有的API
// withRouter的返回值是一个新组件
export default withRouter(Header)
默认路由表示进入到页面后就能匹配到的路由,并展示对应的组件
<Route path="/" component={Login}></Route>
严格匹配不要随便开启,需要再开,有些时候开启会导致无法继续匹配二级路由
class App extends React.Component {
render() {
return (
<Router>
<div>
<Link to="/">首页</Link>
<Link to="/login">登录页面</Link>
{/* 此时该组件只会匹配"/"这一种情况 */}
<Route path="/" exact component={Home}></Route>
<Route path="/login" component={Login}></Route>
</div>
</Router>
)
}
}
默认情况下,路由的切换是push模式,点击后退按钮时还可以回到上一个路由。如果想要开启replace模式,需要在Link组件上添加replace属性
<Link to="/home" replace></Link>
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: 1,
title: '消息1',
},
{
id: 2,
title: '消息2',
},
{
id: 3,
title: '消息3',
},
],
}
render() {
return (
<div>
<ul>
{this.state.messageArr.map((msgObj) => {
return (
<li key={msgObj.id}>
{/* 1、向路由组件传递params参数 */}
<Link to={`/home/message/detail/${msgObj.id}/${msgObj.title}`}>
{msgObj.title}
</Link>
</li>
)
})}
</ul>
{/* 2、声明接受params参数 */}
<Route path="/home/message/detail/:id/:title" component={Detail} />
</div>
)
}
}
接收params参数
import React, { Component } from 'react'
const detailData = [
{ id: 1, content: '内容1' },
{ id: 2, content: '内容2' },
{ id: 3, content: '内容3' },
]
export default class Detail extends Component {
render() {
console.log('detail:', this.props)
// 接收params参数
const { id, title } = this.props.match.params
let index = parseInt(id) - 1
return (
<ul>
<li>id:{id}</li>
<li>title:{title}</li>
<li>content:{detailData[index].content}</li>
</ul>
)
}
}
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: 1,
title: '消息1',
},
{
id: 2,
title: '消息2',
},
{
id: 3,
title: '消息3',
},
],
}
render() {
return (
<div>
<ul>
{this.state.messageArr.map((msgObj) => {
return (
<li key={msgObj.id}>
{/* 1、向路由组件传递search参数 */}
<Link to={`/home/message/detail?id=${msgObj.id}&title=${msgObj.title}`}>
{msgObj.title}
</Link>
</li>
)
})}
</ul>
{/* 2、search参数无须声明接收,正常注册路由即可 */}
<Route path="/home/message/detail" component={Detail} />
</div>
)
}
}
接收参数
import React, { Component } from 'react'
import qs from 'querystring'
let obj = { id: 1, name: 'mingming' }
console.log(qs.stringify(obj)) // id=1&name=mingming 这是urlencoded格式
let query = 'id=1&name=mingming'
console.log(qs.parse(query)) // {id: "1", name: "mingming"}
const detailData = [
{ id: 1, content: '内容1' },
{ id: 2, content: '内容2' },
{ id: 3, content: '内容3' },
]
export default class Detail extends Component {
render() {
console.log('detail:', this.props)
// 3、接收search参数
const { id, title } = qs.parse(this.props.location.search.slice(1))
let index = parseInt(id) - 1
return (
<ul>
<li>id:{id}</li>
<li>title:{title}</li>
<li>content:{detailData[index].content}</li>
</ul>
)
}
}
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: 1,
title: '消息1',
},
{
id: 2,
title: '消息2',
},
{
id: 3,
title: '消息3',
},
],
}
render() {
return (
<div>
<ul>
{this.state.messageArr.map((msgObj) => {
return (
<li key={msgObj.id}>
{/* 1、向路由组件传递state参数 */}
<Link
to={{
pathname: '/home/message/detail',
state: { id: msgObj.id, title: msgObj.title },
}}>
{msgObj.title}
</Link>
</li>
)
})}
</ul>
{/* 2、state参数无须声明接收,正常注册路由即可 */}
<Route path="/home/message/detail" component={Detail} />
</div>
)
}
}
接收参数
import React, { Component } from 'react'
const detailData = [
{ id: 1, content: '内容1' },
{ id: 2, content: '内容2' },
{ id: 3, content: '内容3' },
]
export default class Detail extends Component {
render() {
console.log('detail:', this.props)
// 接收state参数
// 如果清空history对象或者清除浏览器的历史记录,此时刷新页面会报错state是undefined
const { id, title } = this.props.location.state || {}
let msgObj = detailData.find((item) => item.id === id) || {}
return (
<ul>
<li>id:{id}</li>
<li>title:{title}</li>
<li>content:{msgObj.content}</li>
</ul>
)
}
}
pushShow = (msgObj) => {
// 传递params参数
this.props.history.push(`/home/message/detail/${msgObj.id}/${msgObj.title}`)
// 传递search参数
this.props.history.push(`/home/message/detail?id=${msgObj.id}&title=${msgObj.title}`)
// 传递state参数
this.props.history.push(`/home/message/detail`, {
id: msgObj.id,
title: msgObj.title,
})
}
前端学习交流QQ群,群内学习讨论的氛围很好,大佬云集,期待你的加入:862748629,点我加入