对照代码查看笔记: 代码详情
- 有详细的ReadMe文档
四、 路由
单页面 多组件: 整个应用只有一个完整的页面,局部更新
4-1
4-1-1
- 路由:
- 一个映射关系(key:value)
- key为路径,value可能是function或components
- 后端路由:
- value是function,用来处理客户端提交的请求
- 注册路由:router.get(path,function(req,res))
- 工作过程:当node接收一个请求时,根据请求路径找到匹配的路由,调用路由中的函数来处理请求,返回响应数据
- 前端路由:
- 浏览器端路由:value是component,用来展示页面内容
- 注册路由:
<Route path="/test" component={Test}>
- 工作过程:当浏览器的path变为/test时,当前路由组件就会变为Test组件
4-2 react-router-dom
印记中文
4-2-1 插件库
-
- react的一个插件库
- 专门用来实现一个SPA应用
- 基于react的项目基本都会用到此库
yarn add react-router-dom
4-2-2 内置组件:
一、
<BroswerRouter>
<HashRouter>
<Route>
<Redirect>
<Link>
<NavLink>
<Switch>
二、
- history对象
- match对象
- withRouter函数
4-2-3 路由的基本使用
react-router-dom@6
react-router-dom@5
-
明确好界面中的导航区、展示区
-
导航区的a标签改为Link标签
<Link to="/xxx">Demo</Link>
-
展示区写Route标签进行路径的匹配
<Route path="/xxx" component={Demo}>
-
<App>
的最外侧包裹了<HashRouter>
或<BrowserRouter>
4-2-4 路由组件和一般组件的区别
- 路由组件在pages中
- 一般组件在components中
4-2-4-1 路由组件
-
About组件
import React, { Component } from "react"; export default class index extends Component { render() { console.log("About组件收到的props是", this.props); return <div>About</div>; } }
-
App.jsx中的about是路由组件
import React, { Component } from "react"; import { Link, BrowserRouter, Route } from "react-router-dom"; import Home from "./pages/Home"; // Home是路由组件 import About from "./pages/About"; // About是路由组件 <BrowserRouter> <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> <Route path="/home" component={Home}></Route> </div> </div> </div> </BrowserRouter>
4-2-4-2 区别
-
写法不同:
- 一般组件:
<Demo/>
- 路由组件:
<Route path="/demo" componet = {Demo}>
- 一般组件:
-
存放位置不同:
- 一般组件:
components
- 路由组件:pages
- 一般组件:
-
接收到的props不同:
- 一般组件:写组件标签时传递了什么,就能收到什么
- 路由组件:接收到三个固定的属性
history: go: ƒ go(n) goBack: ƒ goBack() goForward: ƒ goForward() push: f push(path, state) replace: f replace(path, state) location: pathname: "/about" search: "" state: undefined match: params: {} path: "/about" url: "/about"
4-2-5 NavLink的使用
-
<NavLink activeClassName="active" className="list-group-item" to="/about" > About </NavLink> <NavLink activeClassName="active" className="list-group-item" to="/home" >
-
封装NavLink组件
- NavLink可以实现路由链接的高亮,通过activeClassName指定样式名
- 标签体内容是一个特殊的标签属性
- 通过this.props.children可以获取标签体内容
-
封装的NavLink组件
-
MyNavLink组件:
import React, { Component } from "react"; import { NavLink } from "react-router-dom"; export default class MyNavLink extends Component { render() { console.log(this.props); // const { to, title, a, b } = this.props; return ( <div> <NavLink activeClassName="active" className="list-group-item" {...this.props} > {/* {this.props.children} */} </NavLink> </div> ); } }
-
App.jsx
import React, { Component } from "react"; import { BrowserRouter, Route } from "react-router-dom"; import Home from "./pages/Home"; // Home是路由组件 import About from "./pages/About"; // About是路由组件 import Header from "./components/Header"; // Header是一般组件 import MyNavLink from "./components/MyNavLink/index"; <div className="list-group"> {/* React中靠路由链接实现切换组件 */} <MyNavLink to="/home" title="Home" a="jj" b="hh"> Home </MyNavLink> <MyNavLink to="/about">About</MyNavLink> <MyNavLink to="/about" children="about"> About </MyNavLink> </div>
-
4-2-6 Switch的使用
-
- 通常情况下,path和component是一一对应的关系
- Switch可以提高路由匹配效率(单一匹配)
-
import { BrowserRouter, Route, Switch } from "react-router-dom"; <Sw itch> {/* 注册路由 */} <Route path="/about" component={About}></Route> <Route path="/home" component={Home}></Route> <Route path="/home" component={Test}></Route> </Switch>
-
4-2-7 解决多级路径刷新页面样式丢失的问题
-
public/index.html 中 引入样式时不写 ./ ,写/
-
public/index.html 中引入样式不屑 ./ ,写%PUBLIC_URL%(常用)
-
使用HashRouter
-
public文件夹相当于localhost的根路径
-
http://localhost:3000/css/bootstrap.css http://localhost:3000/ttt/css/bootstrap.css
-
4-2-8 路由的模糊匹配与严格匹配
-
Linke的to必须包含 路由组件的path
<Link to="/home/a/b"></Link> <Route path="/home" component={home}> /* 模糊匹配 */
- 【输入的路径】必须包含【匹配的路径】,且顺序要一致
-
严格匹配
<Route exact={true} path="/home" component={home}>
- 严格匹配不要随便开启
4-2-9 Redirect的使用
- React 6.0:
<Route path="*" element={<Navigate to="/about" />} />
<Redirect to="/home">
- 一般写在所有路由的最下方,当所有路由都无法匹配时候,跳转到Redirect指定的路由
- 严格匹配不要随便开启,需要再开,有些时候开启会导致无法继续匹配二级路由
4-3 嵌套路由
- 注册子路由时要写上父路由的path值
- 路由的匹配是按照注册路由的顺序进行的
4-3-1 向路由组件传递参数数据
4-3-1-1 向路由组件传递params参数
-
params参数:
-
路由链接(携带参数) :
<Link to="/demo/test/tome/18">详情</Link>
-
注册路由(生命接收) :
<Route path="demo/test/:name/:age" component={Test}></Route>
-
接收参数:
const {id,title} = this.props.match.params
-
-
Message向Detail组件传递数据
-
Message
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" }, ], }; render() { const { messageArr } = this.state; return ( <div> <div> <div className="row"> <div> <ul> {messageArr.map((msgObj) => { return ( <li key={msgObj.id}> {/* 向路由组件传递params参数 */} <Link to={`/home/message/detail/${msgObj.id}/${msgObj.title}`} > {msgObj.title} </Link </li> ); })} </ul> <hr /> {/* 声明接收params参数 */} <Route path="/home/message/detail/:id/:title" component={Detail} ></Route> </div> </div> </div> </div> ); } }
-
Detail
import React, { Component } from "react"; import qs from "query-string"; let obj = { name: "tom", age: 18 }; // key=value & key=value console.log(qs.stringify(obj)); // let str = "carName=奔驰&price=199"; const DetailData = [ { id: "01", content: "你好,中国" }, { id: "02", content: "你好,aaa" }, { id: "03", content: "你好,abbb" }, ]; export default class Detail extends Component { render() { console.log(this.props); const { id, title } = this.props.match.params; // 接收params参数 const findResult = DetailData.find((detailObj) => { return detailObj.id === id; }); return ( <div> <ul> <li>ID: {id}</li> <li>Title: {title}</li> <li>CONTENT: {findResult.content}</li> </ul> </div> ); } }
-
4-3-1-3 向路由组件传递search参数
-
-
路由链接(携带参数):
<Link to='/demo/test?name=tome&age=18'>详情</Link>
-
注册路由(无需声明,正常注册即可):
<Route path="/demo/test" componnet={Test}>
-
接收参数:
this.props.location.search
-
备注:获取到的search是urlencode编码字符串,需要借助querytring解析
-
-
-
Message
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" }, ], }; render() { const { messageArr } = this.state; return ( <div> <div> <div className="row"> <div> <ul> {messageArr.map((msgObj) => { return ( <li key={msgObj.id}> {/* 向路由组件传递search参数 */} <Link to={`/home/message/detail/?id=${msgObj.id}&title=${msgObj.title}`} > {msgObj.title} </Link> </li> ); })} </ul> <hr /> {/* 生命接受search参数 */} <Route path="/home/message/detail" component={Detail}></Route> </div> </div> </div> </div> ); } }
-
Detail
import React, { Component } from "react"; import qs from "querystring"; let obj = { name: "tom", age: 18 }; // key=value & key=value console.log(qs.stringify(obj)); const DetailData = [ { id: "01", content: "你好,中国" }, { id: "02", content: "你好,aaa" }, { id: "03", content: "你好,abbb" }, ]; 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 ( <div> <ul> <li>ID: {id}</li> <li>Title: {title}</li> <li>CONTENT: {findResult.content}</li> </ul> </div> ); } }
-
4-3-1-4 向路由组件传递state参数
-
路由链接:
<Link to={{path:'/demo/test?name=tome&age=18'}}>详情</Link>
-
注册路由(无需声明,正常注册即可):
<Route path="/demo/test" componnet={Test}>
-
接收参数:
this.props.location.state
-
备注:刷新也可以保留住参数
4-3-2 多种路由跳转方式
4-3-2-1
-
push和replace
<Link replace to='/home'/>
- 登录成功之后,不准用户返回登陆界面
4-4 其他
4-4-1 push与replace
<Link replace={true} to={{path:'/demo/test?name+tome&age=18'}}>详情</Link>
4-4-2 编程式路由导航
不用Link,也不用NavLink
一、 携带params参数
-
编写代码,事件触发之后执行,自动跳转到Detail组件,且为replace跳转
this.props.history.replace(`/home/message/detail/${id}/${title}`)
-
this.props.history.push(`/home/message/detail/${id}/${title}`)
二、 携带search参数
this.props.hostory.replace(`/home/message/detail?id=${id}&title=${title}`)
三、 state参数
this.props.history.replace(`/home/message/detail`)
四、
-
replaceShow = (id,title) => { // 编写代码,replace跳转至 Detail(携带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}) } <button onClick={ () => this.replaceShow(msgObj.id,msgObj.title) }>replace查看</button>
4-4-3 withRouter的使用
-
- withRouter可以加工一般组件,让一般组件具备路由组件所特有的api
- withRouter的返回值是一个新组件
-
- goback()
- go()
- goforward()
- push
- replace
-
import {withRouter} from 'react-router-dom' back = () => { this.props.history.goBack() } <button onCLick={this.back}>回退</button>
4-4-4 BrowserRouter与HashRouter
- 底层原理不一样:
- BrowserRouter使用的是H5的history API,不兼容IE9及以下版本
- HshRouter使用的是URL的哈希值
- URL表现形式不一样
- BrowserRouter的路径中没有#
- 刷新后对路由state参数的影响
- BrowserRouter没有任何影响,因为state保存在history对象中
- HashRouter刷新后会导致路由state参数的丢失
- HashRouter可以用于解决一些路径错误相关的问题
4-5 ReactRouter6教程
4-5-1 概述
- React Router以三个不同的包发布到npm上,分别为:
- react-router: 路由的核心库,提供了:组件、钩子
- react-touter-dom: 包含react-router所有内容,并添加一些专门用于DOM的组件
<BrowserRouter>
等
- react-router-native:
- 包含react-router所有内容,并添加一些专门用于ReactNative的API,
<NativeRouter>
等
- 包含react-router所有内容,并添加一些专门用于ReactNative的API,
- 与React Router5.x版本比:
- 内置组件的变化:移除
<Switch>
,新增<Routes>
等 - 语法的变化 :
<component={About}>
变为element={<About>}
等 - 新增多个hook:usePrams/useNavigate/useMatch
- 内置组件的变化:移除
Component
4-5-1-1 <Routes/>
与<Route/>
-
<Routes/>
与<Route/>
<Route caseSensitive>
属性用于指定: 匹配时是否区分大小写<Route>
也可以嵌套使用,且可配合useRoutes()
配置“路由表”,但需要通过<Outlet>
组件来渲染子路由
-
例子:
<div className="row"> <div className="col-xs-2 col-xs-offset-2"> <div className="list-group"> {/* React中靠路由链接实现切换组件 */} <NavLink className="list-group-item" to="/about">About</NavLink> <NavLink className="list-group-item" to="/home">Home</NavLink> </div> </div> <div className="col-xs-6"> <div className="panel"> <div className="panel-body"> <Routes> {/* 注册路由 */} <Route path="/about" element={<About/>}/> <Route path="/home" element={<Home />} /> {/* <Route path="/home" element={<Demo/>}/> */} <Route path="/" element={<Navigate to="/about"/>} /> {/* <Redirect to="/home"></Redirect> */} </Routes> </div> </div> </div> </div>
4-5-1-2 <NavLink>
-
与
<Link>
组件类似,可以实现导航的“高亮”效果 -
// NavLink默认类名是active,指定自定义的class <NavLink to="login" className={({ isActive }) => { console.log('home',isActive) return isActive ? 'base one' : 'base' }>login</NavLink> // 默认情况下,home的子组件匹配成功,Home的导航也会高亮 当NavLink上添加了end属性后,若Home的子组件匹配成功,则Home的导航没有高亮效果 <NavLink to="home" end>Home</NavLink>
4-5-1-3 <Navigate>
-
只要
<Navigate>
组件被渲染,就会修改路径,切换视图 -
replace属性用于控制跳转模式(push/replace)
-
<Navgate to="/about" replace={true}>
4-5-2 Hooks
4-5-2-1 useRoutes路由表
-
routes下的index.js
import About from '../pages/About' import Home from '../pages/Home' import { Navigate } from 'react-router-dom' export default [ { path: '/about', element: <About></About> }, { path: '/home', element: <Home></Home> }, { path: '/', element: <Navigate to="/about"/> } ]
-
App组件
const element = useRoutes([ { path: '/about', element: <About></About> }, { path: '/home', element: <Home></Home> }, { path: '/', element: <Navigate to="/about"/> } ]) <div className="panel-body"> {/* 注册路由 */} {element} </div>
4-5-2-2 useNavigate()
-
作用:返回一个函数用来实现编程式导航
-
navigate('/login',{ replace: false, state: {a:1, b:2} }) navigate(-1)
4-5-2-3 useParams()
-
返回当前匹配路由的params参数,
match.params
-
4-5-2-4 useSearchParams()
- 作用: 用于读取和修改当前位置的URL中的查询字符串
- 返回一个包含两个值的数组,内容:当前search参数、更新search中的函数
4-5-2-5 useLocation()
- 获取当前location信息,对标5.x中的路由组建的location属性
4-5-2 重定向
<Route path="/" element={<Navigate to="/about"/>} />