前端路由原理
路由是通过bom的history实现的,可以通过以下的方式实现
<a href="http://www.baidu.com" onclick="return push('/test1')"></a>
<script type="text/javascript" src="https://cdn.bootcss.com/history/4.7.2/history.js"></script>
let history = History.createBrowserHistory()
function push(path){
history.push()
}
路由的基本使用
更准确的说是react-router-dom的基本使用(区别于react-router)
react-router-dom简介
1、react的插件库
2、专门用来实现一个SPA项目
3、基本所有的项目都要用到此库
安装
npm install react-router-dom
引入
// 引入Link和Router
import { Link, Route } from 'react-router-dom'
// 引入路由组件
import About from './components/About'
import Home from './components/Home'
使用
<Link className="list-group-item" to="/about">about</Link>
<Link className="list-group-item" to="/home">home</Link>
<Route path="/about" component={About}/>
<Route path="/home" component={Home}/>
注意,,要包在BrowserRouter下,还需要在Link和Router外面包同一个BrowserRouter。
但是这样的写法比较麻烦,一般采用在App外面包一个全局的BrowserRouter的方式。 如下:
import { BrowserRouter } from 'react-router-dom'
<BrowserRouter>
<App />
</BrowserRouter>,
路由组件一般写在pages文件夹下。
路由组件和一般组件
1、写法不同:
一般组件
路由组件
2、存放位置不同:
一般组件:components文件夹下
路由组件:pages文件夹下
3、接收到的props不同:
一般组件:组件写了什么,就收到什么
路由组件:接收到三个固定的属性
- history:
-
- go: ƒ go(n)
- goBack: ƒ goBack()
- goForward: ƒ goForward()
- push: ƒ push(path, state)
- replace: ƒ replace(path, state)
- location:
-
- hash: ""
- pathname: "/about"
- search: ""
- match:
-
- params: {}
- path: "/about"
- url: "/about"
NavLink的使用
<NavLink activeClassName="aaa" className="list-group-item" to="/home">home</NavLink>
可以通过activeClassName设置当前选中状态下的链接的高亮显示样式
NavLink封装
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} = this.props
return(
<NavLink activeClassName="aaa" className="list-group-item" {...this.props}/>
)
}
}
<MyNavlink to='/about'>about</MyNavlink>
<MyNavlink to='/home'>home</MyNavlink>
这里需要注意的是上面home把home写在中间作为标签体的写法,值会存储到this.props.home中去。myNavlink封装的函数中,标签体可以通过this.props.children拿到。 {...this.props}这种结构赋值的写法可以一次将所有的属性全部加上,包括chidren属性。
Switch组件
如果不用switch包裹,Route会一直匹配下去,如下方代码,如果写了两个/home匹配的路由,那么下面的Home组件和Test组件都会被渲染。
<Route path="/about" component={About}/>
<Route path="/home" component={Home}/>
<Route path="/home" component={Test}/>
你可能会说我们实际开发不可能写两个一样名称的路由,渲染不一样的组件。即使不会像上面的例子这样写,匹配成功之后还要接着往下匹配也会影响性能。绝大部分情况下,我们都是想匹配成功第一个,就结束后面的匹配。
Switch组件可以帮助我们实现这种惰性匹配,只要匹配成功一个就不再接着往下匹配。
<Switch>
<Route path="/about" component={About}/>
<Route path="/home" component={Home}/>
<Route path="/home" component={test}/>
</Switch>
解决样式丢失问题
根路径问题
我们的react脚手架项目实际上是通过webpack中的devServer服务起的,其根路径实际上对应的我们项目中的public,例如http://localhost:3000/about中http://localhost:3000的根路径实际上就对应脚手架项目中的public。
请求资源不存在的情况
如果我们请求的资源并不存在,那么会去找public文件夹下的index.html来兜底。
样式为什么会丢失
在用二级路由我们是这样写的:
<Navlink to='baidu/about'>about</Navlink>
<Navlink to='baidu/home'>home</Navlink>
<Switch>
<Route path="baidu/about" component={About}/>
<Route path="baidu/home" component={Home}/>
</Switch>
我们的应用bootStrap是这样引的:
<link rel="stylesheet" href="./bootStrap/bootstrap.css">
正确的资源请求路径应该是下面的路径:
http://localhost:3000/bootStrap/bootstrap.css
但是我们刷新页面,请求路径变成了:
http://localhost:3000/baidu/bootStrap/bootstrap.css
这个路径下botStrap资源并不存在,所以就用public下的index.html兜底,所以就造成了样式的丢失。
解决办法:
1、去掉引用的‘.’,"/bootStrap/bootstrap.css"。因为./表示的是当前路径,去掉'.'则表示的绝对路径,直接去localhost:3000下去找。
2、用‘%PUBLIC_URL%/bootStrap/bootstrap.css’
3、换成hash路由,因为hash路由#后面的参数都不会被作为请求参数传过去,请求只会发#号前面的部分。
路由的精准匹配和模糊匹配
模糊匹配
默认的路由进行模糊匹配,在Navlink中加多层路由,可以匹配上相应的组件。如下面的例子‘/about/a’是可以匹配上About组件的,反之,Route中路由为/about/a,Navlink中路由为/about则匹配不上。
<Navlink to='/about/a'>about</Navlink>
<Route path="/about" component={About}/>
精准匹配
可以通过在Route中添加exact="true"属性(也可以直接写exact),设置精准匹配,路由只有完全对应上才可以匹配成功。ps:严格匹配慎用,可能会导致无法匹配二级路由。
<Route path="/about" exact="true" component={About}/>
Redirect
一般写在路由的最下方,当所有路由都无法匹配的时候,跳转到Redirect指定的路由。
<Switch>
<Route path="/about" component={About}/>
<Route path="/home" component={Home}/>
<Redirect to="/about"></Redirect>
</Switch>
嵌套路由
1、注册子路由时要注意写上父路由的path值
2、路由的匹配顺序是按照路由注册顺序进行的
<ul className="nav nav-tabs">
<li>
<Link to="/home/news">News</Link>
</li>
<li>
<Link to="/home/message">Message</Link>
</li>
</ul>
<Switch>
<Route path="/home/news" component={News} />
<Route path="/home/message" component={Message} />
</Switch>
向路由组件传递params参数
params
1、路由链接(携带参数):
<Link to={`/home/message/detail/${msgObj.id}/${msgObj.title}`}>
2、注册路由(声明接收)
<Route path="/home/message/detail/:id/:title" component={Detail}/>
3、接收参数
this.props.match.params
search
1、路由链接
<Link to={`/home/message/detail/?id=${msgObj.id}&title=${msgObj.title}`}>{msgObj.title}</Link>
2、路由注册
无需声明,直接注册即可
<Route path="/home/message/detail" component={Detail}/>
3、接收参数
在this.props.location.search中获取。
state
1、路由链接:
<Link to={{pathname:'/home/message/detail/',state:{id:123,title:‘zhangsan’}}}>{msgObj.title}</Link>
2、路由注册
<Route path="/home/message/detail" component={Detail}/>
3、接收参数
在this.props.location.state中获取。
开启Replace模式
在link上添加replace属性即可。
<Link replace to={{pathname:'/home/message/detail/',state:{id:msgObj.id,title:msgObj.title}}}>{msgObj.title}</Link>
编程式路由
以按钮出发路由跳转为例:
<button onClick={()=>{this.handlePush(msgObj.id,msgObj.title)}}>push</button>
// params形式传递参数
// this.props.history.push(`/home/message/detail/${id}/${title}`)
// search形式传递参数
// this.props.history.push({pathName: `/home/message/detail/?id=${id}&title=${title}`})
// state形式传递参数
this.props.history.push({pathName:'/home/message/detail/',state:{id,title}})
参数的接收同上方通过Link形式传递。
withRouter
withRouter的作用是使得非路由组件也能在props上有history属性,用于编程式路由
1、在非路由组件中引入
import { withRouter } from 'react-router-dom'
2、以withRouter包裹的形式暴露出去
export default withRouter(Header)
BrowserRouter和HashRouter的区别
1、底层原理不一样:
BrowserRouter使用的是H5的history API,不兼容IE9及其以下版本,HashRouter使用的是URL的哈希值。
2、path的表现形式不一样
BrowserRouter的路径中没有#,HashRouter的路径中包含#。
3、刷新后对路由state参数的影响
(1)BrowerRouter没有任何的影响,因为state保存在history对象中,只要浏览器还打开,刷新并不会导致参数丢失。
(2)HashRouter刷新后会导致state参数的丢失。
4、HashRouter可以用于解决一些路径错误相关的问题。