官方网站
一、简介
路由最基本的职责就是当页面的访问地址与Route上的 path匹配时,就渲染出对应的 UI 界面(组件)。 实现SPA应用,页面切换不会刷新页面,内容局部更新。同时路由也可以带货(传参)。
二、react-router提供了两种路由模块
1.React-Router
提供了一些router的核心API,包括Router, Route, Switch等,但是它没有提供 DOM 操作进行跳转的API。
2.React-Router-DOM*
提供了 BrowserRouter,HashRouter , Route, Link等 API,我们可以通过 DOM 的事件控制路由,例如点击一个按钮进行跳转。
所以在开发过程中,我们更多是使用React-Router-DOM。
三、路由的两种模式
1.HashRouter
url中会有个#,例如localhost:3000/#,HashRouter就会出现这种情况,它是通过hash值来对路由进行控制。如果使用HashRouter,地址栏就会默认有这个#。
2.BrowserRouter*
如果不需要这个#,需要优雅的跳转,这时就使用BrowserRouter。
在开发过程中整个SPA项目中,只能存在一个BrowserRouter或者HashRouter标签,否则React在进行路由时就不能正常渲染,需要刷新才能渲染。(这不就与SPA背道而驰…)
四、路由跳转
1.声明式导航
a.Link组件
//1.直接在to属性后写要去的地址
<Link to="/About">去About页面</Link>
<Link to={pathname:'/About'}>去About页面</Link>
Link组件主要属性是to,to可以接受string或者一个object(如果是object要写成对象的形式,键名为pathname),来控制url。
b.NavLink组件
<NavLink to="/About" activeClassName="red">去About</NavLink>
<NavLink to={pathname:'/about'} activeClassName="red">去About</NavLink>
NavLink组件可以为当前选中的路由设置激活时的类名(activeClassName)、样式(activeStyle);以及回调函数等。
2.编程式导航
如果页面不是跳转过来的,或者不是有状态组件,直接使用编程式导航会存在不能跳转的问题。
五、路由的配置
1.Route组件
Route组件用来表示路径和组件的对应关系,当访问地址与Route中的path对应时,就在它的位置渲染出对应的组件。
<BrowserRouter>
//此处BrowserRouter只是例子,实际项目中直接把它放到根文件中
<Route path="/" component={App}/>
<Route path="/About" component={About}/>
</BrowserRouter>
在SPA项目中,只能存在一个BrowserRouter标签,因此一般这个BrowserRouter标签或HashRouter会写在根文件即index.js下包裹住所有组件。
六、基本使用步骤
1.下载路由模块
npm install --save react-router-dom
2.在跳转处设置
a.声明式
//开始先引入模块
import { Link, NavLink } from "react-router-dom";
//有状态组件的render函数中,或无状态组件的return中设置Link或NavLink组件
<Link to="/User">User组件</Link>
<NavLink to="/Goods">Goods组件</NavLink>
//这里to属性的值是自定义的,只要与显示处Route的to属性值匹配即可
如果把link标签写成a标签,那么页面会有闪动,因为浏览器会识别为这是一次请求,刷新页面。而l点击ink标签时,url会更新,组件也会被重新渲染,但页面不会刷新。
b.编程式
import React from "react";
export default class User extends React.Component {
constructor(props) {
super(props)
}
go() {
this.props.history.push({ pathname: '/Goods' })
}
render() {
return (
<div>
这里是User组件
<input type="button" value="走你,去Goods" onClick={() => this.go()} />
</div>
);
}
}
3.在展示处设置
//根文件 index.js
//因为整个项目中只能使用一个BrowserRouter或HashRouter组件,因此直接把它放在根组件之中
import { BrowserRouter } from "react-router-dom";
ReactDOM.render(
<React.StrictMode>
<BrowserRouter>
<App />
</BrowserRouter>
</React.StrictMode>,
document.getElementById('root')
);
//开始先引入模块
import { BrowserRouter, Route } from "react-router-dom";
//有状态组件的render函数中,或无状态组件的return中设置BrowserRouter或HashRouter组件
<Route path="/" component={App}/>
<Route path="/Goods" component={Goods}/>
<Route path="/User" component={User}/>
七、二级路由
直接在父组件里写即可,子组件不用做任何更改
import React from "react";
import { Route, Link } from "react-router-dom";
import User_son1 from "../components/User_son1"
import User_son2 from "../components/User_son2"
export default class User extends React.Component {
constructor(props) {
super(props)
}
render() {
return (
<div>
这里是User组件<br />
{/* Link组件的to属性一定带上父一级路由的路径即可 */}
<Link to="/User/User_son1">显示User_son1</Link> | <Link to="/User/User_son2">显示User_son2</Link>
<hr />
{/* 显示子组件时,也要加上父组件的路径 */}
<Route path="/User/User_son1" component={User_son1}></Route>
<Route path="/User/User_son2" component={User_son2}></Route>
</div>
);
}
}
八、路由传参
1.params
传出值的组件
import React from "react";
import { Route, Link, NavLink } from "react-router-dom";
import UserDetil from "../components/UserDetil"
export default class User extends React.Component {
constructor(props) {
super(props)
}
goparams() {
this.props.history.push('/User/UserDetil/' + '003')
}
render() {
return (
<div>
这里是User组件,路由跳转params带货<br />
{/* 点击的位置,此处配置路由跳转,传值 */}
<Link to="/User/UserDetil/001">显示User1的详情</Link> |
<NavLink to={"/User/UserDetil/002"}>显示User2的详情</NavLink> |
<input type="button" value="显示User3的详情" onClick={() => this.goparams()} />
<hr />
{/* 展示的位置,此处配置路由传值的变量名 */}
<Route path="/User/UserDetil/:userid" component={UserDetil}></Route>
</div>
);
}
}
接收值的组件
import React from "react";
export default class UserDetil extends React.Component {
constructor(props) {
super(props)
}
render() {
return (
<div>
这里是UserDetil组件<br />
{/* 获取值通过this.props.match.params.变量名 获取,函数式组件没有this,在定义函数的形参中声明props即可获取props */}
<p>用户id{this.props.match.params.userid}</p>
</div>
);
}
}
优点:刷新页面,参数依然存在;很好理解…地址栏的地址并没有变。
缺点:只能传字符串,而且如果传的值太多的话,url会变得很长。
2.query
传出的组件
import React from "react";
import { Route, Link, NavLink } from "react-router-dom";
import UserDetil from "../components/UserDetil"
export default class User extends React.Component {
constructor(props) {
super(props)
}
goquery() {
this.props.history.push({ pathname: "/User/UserDetil", query: { userid: "01003" } })
}
render() {
return (
<div>
这里是User组件,路由跳转query带货<br />
{/* 点击的位置,此处配置就必须写成对象,跳转地址pathname,传值query{变量名:变量值} */}
{/* 注:两层{{}},第一层{}表示js代码,第二层{}表示这东西是个对象 */}
<Link to={{ pathname: "/User/UserDetil", query: { userid: "01001" } }}>显示User1的详情</Link> |
<NavLink to={{ pathname: "/User/UserDetil", query: { userid: "01002" } }}>显示User2的详情</NavLink> |
<input type="button" value="显示User3的详情" onClick={() => this.goquery()} />
<hr />
{/* 展示的位置,此处路由配置不用变 */}
<Route path="/User/UserDetil" component={UserDetil}></Route>
</div>
);
}
}
传入的组件
import React from "react";
export default class UserDetil extends React.Component {
constructor(props) {
super(props)
}
render() {
return (
<div>
这里是UserDetil组件,接收query带货<br />
<p>用户id{this.props.location.query.userid}</p>
</div>
);
}
}
优点:传参优雅,地址栏不会添加任何东西;传递参数可以传对象。
缺点:一旦页面刷新,参数就会丢失。
3.state*
传出的组件
import React from "react";
import { Route, Link, NavLink } from "react-router-dom";
import UserDetil from "../components/UserDetil"
export default class User extends React.Component {
constructor(props) {
super(props)
}
gostate() {
this.props.history.push({ pathname: "/User/UserDetil", state: { userid: "01003" } })
}
render() {
return (
<div>
这里是User组件,路由跳转state带货<br />
{/* 点击的位置,此处配置就必须写成对象,跳转地址pathname,传值query{变量名:变量值} */}
{/* 注:两层{{}},第一层{}表示js代码,第二层{}表示这东西是个对象 */}
<Link to={{ pathname: "/User/UserDetil", state: { userid: "01001" } }}>显示User1的详情</Link> |
<NavLink to={{ pathname: "/User/UserDetil", state: { userid: "01002" } }}>显示User2的详情</NavLink> |
<input type="button" value="显示User3的详情" onClick={() => this.gostate()} />
<hr />
{/* 展示的位置,此处路由配置不用变 */}
<Route path="/User/UserDetil" component={UserDetil}></Route>
</div>
);
}
}
传入的组件
import React from "react";
export default class UserDetil extends React.Component {
constructor(props) {
super(props)
}
render() {
return (
<div>
这里是UserDetil组件,接收state带货<br />
<p>用户id{this.props.location.state.userid}</p>
</div>
);
}
}
同query差不多,只是属性不一样,而且state传的参数是加密的,query传的参数是公开的,与query相比,只需要把query改为state即可。
优点:传参优雅,地址栏不会添加任何东西;传递参数可以传对象。
缺点:页面刷新,HashRoute方式会丢失参数,BrowserRouter模式不会丢失参数。
4.seach
传出的组件
import React from "react";
import { Route, Link, NavLink } from "react-router-dom";
import UserDetil from "../components/UserDetil"
export default class User extends React.Component {
constructor(props) {
super(props)
}
gosearch() {
this.props.history.push({pathname:'/Inbox',search:'?a=1&b=2'})
}
render() {
return (
<div>
<Link to='/Inbox?a=1&b=2' >显示User1的详情</Link> |
<NavLink to='/Inbox?a=1&b=2' >显示User2的详情</NavLink> |
<input type="button" value="显示User3的详情" onClick={() => this.gosearch()} />
<hr />
{/* 展示的位置,此处路由配置不用变 */}
<Route path="/User/UserDetil" component={UserDetil}></Route>
</div>
);
}
}
取值
this.props.location.search
用location.search所获取的是查询字符串,即获取到的是传来的一长串字符串(例:?a=1&b=2)所以,需要使用search方法时需要自行解析字符串,也可以使用第三方模块:qs,或者nodejs里的query-string。
九、路由上下文
在react-router里面,如果组件是通过路由跳转的,那么它会把路由相关的API挂在了组件的props上,并且分为history,location,match。
history:历史,用来跳转的,并做历史记录。有函数:push(),replace(),go() …………
location:地址,地址栏上路径,并保存着query,state,search等数据。
match:匹配,匹配到的路径,有params
//使用state的跳转方式打印一下props
import React from "react";
export default class UserDetil extends React.Component {
constructor(props) {
super(props)
}
componentDidMount() {
console.log(this.props);
}
render() {
return (
<div>
这里是UserDetil组件,接收state带货<br />
<p>用户id{this.props.location.state.userid}</p>
</div>
);
}
}
//打印结果
props: {
{ history: {… }, location: {… }, match: {… }, staticContext: undefined }
history:
action: "PUSH"
block: ƒ block(prompt)
createHref: ƒ createHref(location)
go: ƒ go(n)
goBack: ƒ goBack()
goForward: ƒ goForward()
length: 50
listen: ƒ listen(listener)
location: { pathname: "/User/UserDetil", state: {… }, search: "", hash: "", key: "h00797" }
push: ƒ push(path, state)
replace: ƒ replace(path, state)
__proto__: Object
location:
hash: ""
key: "h00797"
pathname: "/User/UserDetil"
search: ""
state: { userid: "01001" }
__proto__: Object
match:
isExact: true
params: { }
path: "/User/UserDetil"
url: "/User/UserDetil"
__proto__: Object
staticContext: undefined
__proto__: Object
}
//确实history,location,match三个属性被挂到了props上;示例中是用stste跳转的,所以只有location中有信息。
十、非路由跳转的组件获取上下文
在页面上展示组件有两种方式,一种是通过路由跳转,一种是使用标签名直接展示。
a)使用路由跳转的方式:就算这个组件没有属性,通过路由时,props都会自动增加属性:history,match,location。
b)使用标签名直接展示时:因为没有属性,所以组件的props是空。
如果使用标签名展示组件的方式,还想获取到路由上下文,有以下解决方案:
1.通过属性传递
首先要求,当前父组件是路由跳转过来的(起码得保证他爸有,儿子才具有世袭的可能性),然后把路由上下文通过属性的方式传递给子组件。
2.通过withRouter包装(高阶组件)
import {withRouter} from 'react-router-dom'
class 组件 extends Component{
}
export default withRouter(组件)
withRouter是react-router-dom提供的一个高阶函数,引入解构后就可以使用,通过这个高阶函数,就可以给组件添加上下文,导出时通过withRouter这个高阶函数即可。
使用时正常使用组件,不受任何影响。
十一、Route组件里的各种属性
首先说明:react的路由匹配默认是模糊的,包容的。
1.exact严格匹配
{/* 如果没有exact={true}这个属性,页面会展示两个Goods组件,因为"/Goods"路径也符合"/" */}
<Route exact={true} path="/" component={Goods} />
<Route path="/Goods" component={Goods} />
<Route path="/User" component={User} />
exact={true} 表示地址栏的路径和提供的地址必须相等,一模一样,才会被匹配。
2.Switch排他性匹配
<Switch>
{/* 有Switch之后如果没有exact={true}这个属性,那么所有的路径都指向/,并且不能匹配后面的 */}
<Route exact={true} path="/" component={Goods} />
<Route path="/Goods" component={Goods} />
<Route path="/User" component={User} />
</Switch>
React路由匹配到的多个路由对应的所有组件会同时被显示在页面上。如果只希望显示匹配到的第一个组件(换句话说:匹配到的第一个符合要求的路径后,其它路径就不再做匹配),那么使用switch。使用时,Switch组件包裹住所有Route组件。
3.404
在路由配置里不设置path属性,那么,就总是会匹配上。404页面就需要这样做(当然还得结合Switch)
4.Redirect重定向
如果匹配到from中的路径,那就给你重定向到to的路径去。使用时一定要加exact={true},否则轻易一匹配,所有路径都会被重定向。
详解
1.组件及其作用
组件 | 作用 | |
---|---|---|
路由模式 | BrowserRouter | 约定模式 为 history,使用 HTML5 提供的 history API 来保持 UI 和 URL 的同步 |
路由模式 | HashRouter | 约定模式 为 hash,使用 URL 的 hash 来保持 UI 和URL 的同步 |
声明式跳转 | NavLink | 声明式跳转 还可以约定 路由激活状态 |
声明式跳转 | Link | 声明式跳转 无激活状态 |
重定向 | Redirect | 重定向 ~~ replace |
匹配并展示 | Route | 匹配组件,并展示组件。即匹配成功后,组件立即被替换成匹配的组件 |
排他性匹配 | Switch | 排他性匹配。如果不想使用包容性,那么使用Switch。 |
高阶组件 | withRouter | 把不是通过路由切换过来的组件中,将 history、location、match 三个对象传入props对象上(高阶组件) |
2.结构
BrowserRouter|HashRouter
App(或其它组件)
- NavLink|Link
- Route
- Redirect
- 子组件
- NavLink|Link
- Route
- …
- 子组件
3.BrowserRouter
属性 | 类型 | 作用 |
---|---|---|
basename | string | 所有位置的基本URL。如果您的应用是从服务器上的子目录提供的,则需要将其设置为子目录。格式正确的基本名称应以斜杠开头,但不能以斜杠结尾 |
getUserConfirmation | Function | 用于确认导航的功能。默认使用 。 |
4.Route
属性 | 类型 | 作用 |
---|---|---|
path | string |object | 路由匹配路径。没有path属性的Route 总是会 匹配 |
exact | boolean | 为true时,要求全路径匹配(/home)。路由默认为“包含”的(/和/home都匹配),这意味着多个 Route 可以同时进行匹配和渲染 |
component | Function |component | 在地址匹配的时候React的组件才会被渲染,route props也会随着一起被渲染 |
render | Function | 内联渲染和包装组件,要求要返回目标组件的调用 |
5.Link
属性 | 类型 | 作用 |
---|---|---|
to | string | 对象{pathname:,search:,hash:} | 要跳转的路径或地址 |
replace | boolean | 是否替换历史记录 |
6.NavLink
属性 | 类型 | 作用 |
---|---|---|
to | string|对象{pathname:,search:,hash:} | 要跳转的路径或地址 |
replace | boolean | 是否替换历史记录 |
activeClassName | string | 当元素被选中时,设置选中样式,默认值为 active |
activeStyle | object | 当元素被选中时,设置选中样式 |
7.Switch
该组件用来渲染匹配地址的第一个Route或者Redirect,仅渲染一个路由,排他性路由,默认全匹配(场景:侧边栏,引导选项卡等)
属性 | 类型 | 作用 |
---|---|---|
location | string object | |
children | node |
8.Redirect
该组件用来渲染匹配地址的第一个Route或者Redirect,仅渲染一个路由,排他性路由,默认全匹配(场景:侧边栏和面包屑,引导选项卡等
属性 | 类型 | 作用 |
---|---|---|
from | string | 来自 |
to | string object | 去向 |
push | boolean | 添加历史记录 |
exact | boolean | 严格匹配 |
sensitive | boolean | 区分大小写 |
能看到这里同样辛苦的是你。。。也不枉我24k纯人手。