SPA:单页Web应用(Single Page Application);
整个页面只有一个完整的页面;
点击页面中的链接不会刷新页面,只会做页面的局部刷新;
数据都需要通过ajax请求获取,并在前端异步展现;
即单页面多组件。
路由:一个路由就是一个映射关系(key:value);key为路径,value可能是function或component;
前端路由的基石:history
创建history可以有两种方式:(1)直接使用H5推出的history身上的API;
History.createBrowserHistory()
(2)hash值(锚点);
History.createHashHistory()
react-router:react的一个插件库,专门用来实现一个SPA应用,基于react的应用基本都会用到此库;这里安装的是web应用的版本
首页在项目里安装下载react-router-dom这个库:
npm i react-router-dom
1.路由的基本使用
路由链接要通过路由器来管理,在react库中也有两种路由器:<BrowserRouter>,<HashRouter>,这里先看一下<BrowserRouter>
App.jsx:
import React, { Component } from 'react'
import { Link, Route } from "react-router-dom";
import About from './components/About';
import Home from './components/Home';
export default class App extends Component {
render() {
return (
<div>
<div className="row">
<div className="col-xs-offset-2 col-xs-8">
<div className="page-header"><h2>React Router Demo</h2></div>
</div>
</div>
<div className="row">
<div className="col-xs-2 col-xs-offset-2">
<div className="list-group">
{/* <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>
<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>
</div>
)
}
}
注意:整个应用要交给一个路由器来管理
所以在index.jsx中,直接用<BrowserRouter>把整个App组件包住:
index.jsx:
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')
)
2.一般组件和路由组件
(1).写法不同:一般组件:<Demo/>
路由组件:<Route path="/demo" component={Demo}/>
(2).存放位置不同:一般组件:components
路由组件:pages
(3).接收到的props不同:一般组件:写组件标签时,传递了什么属性就接受到什么属性
路由组件:接受到路由器传递的三个固定的属性:history,location,match;
3.NavLink的使用
NavLink:导航link,可以在点击链接时动态的显示样式效果,默认的样式类名时active,如果想要自定义样式,需要使用activeClassName;
<NavLink activeClassName="pink" className="list-group-item" to="/about">About</NavLink>
<NavLink activeClassName="pink" className="list-group-item" to="/home">Home</NavLink>
注意:NavLink可以实现路由链接的高亮,通过activeClassName指定样式名;
标签体内容是一个特殊的标签属性;通过this.props.children可以获取标签体内容;
<Switch>
<Route path='/about' component={About}/>
<Route path='/home' component={Home}/>
<Route path='/home' component={Test}/>
</Switch>
通常情况下,to和path时一一对应的关系,为了提高路由的匹配效率(单一匹配),可以使用<Switch>标签;
4.解决样式丢失问题的三种方式:
(1)、在public文件夹下的index.html中:<link href="./css/bootstrap.css" rel="stylesheet"/>改为<link href="/css/bootstrap.css" rel="stylesheet"/>
(2)、在public文件夹下的index.html中:<link href="./css/bootstrap.css" rel="stylesheet"/>改为<link href="%PUBLIC_URL%/css/bootstrap.css" rel="stylesheet"/>
(3)、使用<HashRouter>包裹应用,不推荐
5.路由的严格匹配和模糊匹配:
(1)、默认使用的时模糊匹配(简单记:【输入的路径】必须要包含要【匹配的路径】,且顺序要一致)
(2)、开启严格匹配:<Route exact={true} path='/about' component={About}/> 或者 <Route exact path='/home' component={Home}/>
(3)、严格匹配不要随便开启,需要再开,有些时候开启会导致无法继续匹配二级路由
6.<Redirect/>的使用
(1)、一般写在所有路由注册的最下方,当所有路由都无法匹配时,跳转到redirect指定的路由;
(2)、具体编码:
{/* 注册路由 */}
<Switch>
<Route exact={true} path='/about' component={About}/>
<Route path='/home' component={Home}/>
<Redirect to="/about"/>
</Switch>
7.嵌套路由
(1)、注册子路由时要写上父路由的path值;
(2)、路由的匹配是按照注册路由的顺序执行的;
8.向路由组件传递参数
(1)、params传递参数
路由链接(携带参数):<Link to={`/home/messages/detail/${msgObj.id}/${msgObj.title}`}>
注册路由(声明接受):<Route path="/home/messages/detail/:id/:title" component={Detail}/>
接受参数:const {id, title} = this.props.match.params
(2)、search传递参数
路由链接(携带参数):<Link to={`/home/messages/detail/?id=${msgObj.id}&title=${msgObj.title}`}>{msgObj.title}</Link>
注册路由(无需声明,正常注册即可):<Route path="/home/messages/detail" component={Detail}/>
接受参数:const {search} = this.props.location
注意:获取到的search是urlencode编码字符串,需要借助querystring解析 const {id, title} = qs.parse(search.slice(1))
(3)、state传递参数
路由链接(携带参数):<Link to={{pathname:'/home/messages/detail',state:{id:msgObj.id, title:msgObj.title}}}>{msgObj.title}</Link>
注册路由(无需声明,正常注册即可):<Route path="/home/messages/detail" component={Detail}/>
接受参数:const {id, title} = this.props.location.state
注意:地址栏里不会显示参数,刷新也可以保留住参数,因为他存储在history对象里
9.路由跳转默认是push模式,如果像设置成replace模式时:直接在LInk组件上添加replace属性即可
<Link replace to={{pathname:'/home/messages/detail',state:{id:msgObj.id, title:msgObj.title}}}>
{msgObj.title}
</Link>
10.编程式路由
借助this.props.history对象上的API操作路由跳转,前进,后退
----this.props.history.push('/home/messages')
----this.props.history.replace(`/home/messages/detail`, {id,title})
----this.props.history.goForward();
----this.props.history.goBack();
----this.props.history.go(2);
11.withRouter:可以加工一般组件,让一般组件具备路由组件的API,他的返回值是一个新组件
12:BrowserRouter和HashRouter的区别:
(1)、底层原理不一样:BrowserRouter使用的是H5的history API,不兼容IE9及以下版本。
HashRouter使用的是URL的哈希值
(2)、url表现形式不一样:BrowserRouter的路径中没有#,例如:localhost:3000/demo/test
HashRouter的路径中包含#,例如:localhost:3000/#/demo/test
(3)、刷新后对路由state参数的影响:BrowserRouter没有任何影响,因为state保存在history中
HashRouter刷新后会导致state参数丢失
(4)、HashRouter可以用来解决一些路径错误的问题