React路由
react-router-dom
react
的一个插件库。- 专门用来实现一个
SPA
应用。 - 基于
react
的项目基本都会用到此库。
老版本
声明式导航
-
下载
yarn add react-router-dom@5
-
引入路由切换
由于
react-router-dom
插件对外暴露很多方法,因此推荐按需引入,引入Link
组件。import {Link} from 'react-router-dom' <Link to="/about"></Link>
发现报错,查看错因,显示“不允许在
Router
外使用Link
组件”。Router
中分两种:- 哈希模式:
HashRouter
- 历史模式:
BrowserRouter
import {Link, BrowserRouter} from 'react-router-dom' <BrowserRouter> <Link to="/about"></Link> <Link to="/home"></Link> </BrowserRouter>
- 哈希模式:
-
注册路由(编写路由链接)
<Route path="/about"></Route>
查看效果,又是一样的错误,需要在外侧包裹
Router
,包裹即可。<BrowserRouter> <Route path="/about" component={About}></Route> <Route path="/home" component={Home}></Route> </BrowserRouter>
点击后发现路径路由发生改变,但是显示的内容没有变化。这是因为
BrowserRouter
路由器只能用一个路由器去管理,分开写自然没有效果。解决方法:在
index.js
里包裹。import {BrowserRouter} from 'react-router-dom' ReactDOM.render( <BrowserRouter> <App /> </BrowserRouter>, doument.getElementById('root') )
总结:
- 明确好界面中的导航区和显示区
- 导航区的
a
标签改为link
标签,to
为路径- 显示区写
route
标签进行路径的匹配,path
为路径,component
为显示的那个组件- 在入口文件把
App
用HashRouter
或BrowserRouter
包裹起来
历史模式和哈希模式的区别
- 底层原理不同
- 历史模式使用的是H5的api,不兼容IE9及以下。
- 哈希模式使用的是url的哈希值。
- path表现形式不同
- 历史模式没有#。
- 哈希模式有#。
- 刷新后对路由state参数的影响
- 历史模式没有任何影响,因为state保存在history对象内。
- 哈希模式刷新后数据会丢失!
- 备注:哈希模式可以用于解决一些路径错误相关的问题。
组件NavLink
类名高亮
如果有点击组件显示时有高亮的效果,可以使用 NavLink
组件。
工作原理:使用了 NavLink
组件当前被激活时会添加一个 active
类名。这个类名可以通过 activeClassName
来修改。
<NavLink activeClassName="daodao" to="/home" component={Home}>Home</NavLink>
文本内容
在上面的代码中使用的双标签的形式,在里面放置内容。但对于 NavLink
而言有一个属性 children
,也可以设置这个文本内容。
<NavLink activeClassName="daodao" to="/home" component={Home} children={Home} />
效果依旧能够实现。
封装
- 使用的组件
<myNavLink children={} to=""></myNavLink>
- 封装的组件
<NavLink activeClassName="daodao" {...this.props} />
组件Switch
看一个案例:
<Route path="/home" component={Home}></Route>
<Route path="/home" component={Test}></Route>
在页面中,当路由路径为 /home
时,会把 Home
和 Test
两个组件都渲染出来,说明在路由渲染时,他不会一匹配就停止,而是一直查询下去,如果路由很多则很浪费性能。
此时可以用到 Switch
组件,把 Route
包裹起来即可。
<Switch>
<Route path="/home" component={Home}></Route>
<Route path="/home" component={Test}></Route>
</Switch>
总结:
- 通常情况下,
path
和component
是一一对应的。Switch
可以提高路由匹配效率(单一匹配)
严格匹配与模糊匹配
- 默认使用模糊匹配,输出的路径必须包含要匹配的路径,且顺序要一致。
- 开启严格匹配:
<Route exact path="" component={}></Route>
。 - 严格匹配不要随便开启,需要再开,有时候开启会导致无法继续匹配二级路由。
Redirect
放在路由注册的最下方,作用是重定向,当所有路由都无法匹配时,重定向指向一个路由组件显示。
<Switch>
<Route path="/home" component={Home}></Route>
<Route path="/home" component={Test}></Route>
<Redirect to="/about" />
</Switch>
路由嵌套
路由的匹配是按照注册顺序来查找,因此父组件的路由会先查询,查询到了再查询其子路由。因此要在二级路由前添加一级路由的路径。
路由传参
params参数
- 路由路径上用
params
携带参数<Link to={`/home/${id}`}></Link>
- 设置动态路由声明接收参数
<Route path="/home/:id" component={Home}></Route>
- 子组件通过
this.props.metch.params
拿到想要的数据对象const {id} = this.props.metch.params
search参数
-
路由路径上用
search
传递参数<Link to={`/home/?id=${id}&title=${title}`}></Link>
-
search参数不需要声明接收,正常注册路由即可
<Route path="/home" component={Home}></Route>
-
子组件通过
this.props.location.search
接收search参数拿到的是一个字符串,可以通过一个库来使用。
import qs from 'qs' const {search} = this.props.location const res = qs.parse(search) // {?id: "1", title: "xx"} 把问号干掉即可 const res = qs.parse(search.slice(1)) // {id: "1", title: "xx"}
拓展:qs的使用
- 对象转字符串
const obj = {name:'dao', age:21} console.log(qs.stringiFy(obj)) // name=dao&age=21
- 字符串转对象
const str = name=dao&age=21 console.log(qs.parse(str)) // {name:'dao', age:21}
state参数
- 传递state参数
<Link to={{pathname:'/home', state:{id:id}}}></Link>
- state参数不需要声明接收,正常注册路由即可
<Route path="/home" component={Home}></Route>
- 子组件通过
this.props.location.state
接收state参数const {id} = this.props.location.state || {}
注意:
这种方法地址栏上没有数据路径,刷新之后数据也不会丢失。因为使用了
BrowserRouter
模式的路由,会一直维护history
对象里的数据。而location
对象包括在history
内。
总结
- 实在要说,params使用的更多,state用于参数保密的情况。
- state方法需要额外携带state对象,因此to写成对象形式,其余不需要这么麻烦
编程式导航
params参数
replaceShow = (id) => {
this.props.history.replace(`/home/${id}`)
}
search参数
replaceShow = (id) => {
this.props.history.replace(`/home/?id=${id}`)
}
state参数
replaceShow = (id) => {
this.props.history.replace(`/home`, {id})
}
前进后退
- 前进
forword = () => { this.props.history.goForword() }
- 后退
back = () => { this.props.history.goBack() }
- 指定跳转的页数,括号内填入数值,正数为前进,负数为后退
go = () => { this.props.history.go(n) }
路由组件与一般组件
区别
- 写法不同:一般组件直接引入,写组件标签即可。路由组件引入,通过
Route
组件注册使用。 - 存放文件夹不同:路由组件放在
pages
文件夹,一般组件放在component
文件夹。 - 接收到的
props
不同:一般组件写标签时传递啥就接受到啥;路由组件固定接收三个对象:history
、location
、match
。
withRouter
路由组件的 this.props
才有 history
api对象,一般组件拿不到,如果一般组件想要使用路由的前进后退,可以使用 withRouter
函数。
class Header extends Component {
back = () => {
this.props.history.goBack()
}
}
export default withRouter(Header)
在导出组件时使用 withRouter
函数包裹组件,可以加工一般组件,让一般组件具备路由组件所拥有的属性。
withRouter
的返回值是一个新租件。
新版本
- 下载
yarn add react-router-dom