一、前言
本文介绍React的一个重要部分,就是react-router,它是官方维护的,在开发中极为重要。
二、前端路由的原理
前端路由是如何做到URL和内容进行映射呢?监听URL的改变。
2.1URL的hash
url的hash就是锚点,本质上是改变window.location.href属性,我们可以直接赋值location.hash来改变href,并且页面不会刷新,hash的优势就是兼容性好,但是有一个#,个人认为不是很好看。
<div id="app">
<a href="#/home">home</a>
<a href="#/about">about</a>
<div class="router-view"></div>
</div>
<script>
// 1.获取router-view
const routerViewEl = document.querySelector(".router-view");
// 2.监听hashchange
window.addEventListener("hashchange", () => {
switch(location.hash) {
case "#/home":
routerViewEl.innerHTML = "home";
break;
case "#/about":
routerViewEl.innerHTML = "about";
break;
default:
routerViewEl.innerHTML = "default";
}
})
</script>
复制代码
2.2HTML5的History
history是h5新增的,它共有六种方式可以来改变URL,并且不刷新页面。
- replaceState:替换原来的路径;
- pushState:使用新的路径;
- popState:路径的回退;
- go:向前或向后改变路径;
- forword:向前改变路径;
- back:向后改变路径;
三、react-router
在目前流行的三大框架(react、vue、angular)中,都有属于自己的路由
React Router版本4开始,路由不在集中在一个包中进行管理。
- react-router是router的核心部分代码;
- react-router-dom是用于浏览器的;
- react-router-native是用于原生应用的;
3.1安装react-router
yarn add react-router-dom
安装react-router-dom会自动帮助我们安装react-router的依赖;
3.2Router的基本使用
react-router提供了一些组件供我们使用:
BrowserRouter和HashRouter
- Router中包含了对路径改变的监听,并且会将相应的路径传递给子组件;
- BrowserRouter使用history模式;
- HashRouter使用hash模式;
Link和NavLink:
- 通常路径的跳转是使用Link组件,最终会被渲染成a元素;
- NavLink是在Link基础之上增加了一些样式属性(后续学习);
- to属性:Link中最重要的属性,用于设置跳转到的路径;
Route:
- Route用于路径的匹配;
- path属性:用于设置匹配到的路径;
- component属性:设置匹配到路径后,渲染的组件;
- exact:精准匹配,只有精准匹配到完全一致的路径,才会渲染对应的组件;
import React, { PureComponent } from 'react';
import { BrowserRouter, Route, Link } from 'react-router-dom';
import Home from './pages/home';
import About from './pages/about';
import Profile from './pages/profile';
export default class App extends PureComponent {
render() {
return (
<BrowserRouter>
<Link to="/">首页</Link>
<Link to="/about">关于</Link>
<Link to="/profile">我的</Link>
<Route exact path="/" component={Home} />
<Route path="/about" component={About} />
<Route path="/profile" component={Profile} />
</BrowserRouter>
)
}
}
复制代码
3.3NavLink的使用
NavLink组件提供了路由激活状态时的一些API
- activeStyle:活跃时(匹配时)的样式;
- activeClassName:活跃时添加的class;
- exact:是否精准匹配;
演示activeStyle,路由激活时,显示红色
<NavLink exact to="/" activeStyle={{color: "red"}}>首页</NavLink>
<NavLink to="/about" activeStyle={{color: "red"}}>关于</NavLink>
<NavLink to="/profile" activeStyle={{color: "red"}}>我的</NavLink>
复制代码
在路径匹配上之后,react默认添加了类“active”
.active {
color: red
}
复制代码
自定义class名称:
<NavLink exact to="/" activeClassName="link-active">首页</NavLink>
<NavLink to="/about" activeClassName="link-active">关于</NavLink>
<NavLink to="/profile" activeClassName="link-active">我的</NavLink>
复制代码
.link-active {
color: red
}
复制代码
3.4Switch的使用
react-router中只要是路径被匹配到的Route对应的组件都会被渲染;
但是实际开发中,我们往往希望有一种排他的思想:
- 只要匹配到了第一个,那么后面的就不应该继续匹配了;
- 这个时候我们可以使用Switch来将所有的Route进行包裹即可;
<Switch>
<Route exact path="/" component={Home} />
<Route path="/about" component={About} />
<Route path="/profile" component={Profile} />
<Route path="/:userid" component={User} />
<Route component={NoMatch} />
</Switch>
复制代码
3.5Redirect的使用
Redirect用于路由的重定向,当这个组件出现时,就会执行跳转到对应的to路径中,比如判断用户是否登录。创建一个user.js文件
import React, { PureComponent } from 'react'
import { Redirect } from 'react-router-dom';
export default class User extends PureComponent {
constructor(props) {
super(props);
this.state = {
isLogin: false
}
}
render() {
return this.state.isLogin ? (
<div>
<h2>User</h2>
<h2>用户名: coderwhy</h2>
</div>
): <Redirect to="/login"/>
}
}
复制代码
在App.js中定义路由
<switch>
...其他路由
<Route path="/login" component={Login} />
</switch>
复制代码
3.6路由嵌套
假设在子页面中再包括两个子页面,示例代码如下:
import React, { PureComponent } from 'react';
import { Route, Switch, Link } from 'react-router-dom';
function AboutProduct(props) {
return (
<ul>
<li>商品列表1</li>
<li>商品列表2</li>
<li>商品列表3</li>
</ul>
)
}
function AboutMessage(props) {
return (
<ul>
<li>消息列表1</li>
<li>消息列表2</li>
<li>消息列表3</li>
</ul>
)
}
export default class About extends PureComponent {
render() {
return (
<div>
<Link to="/about">商品</Link>
<Link to="/about/message">消息</Link>
<Switch>
<Route exact path="/about" component={AboutProduct} />
<Route path="/about/message" component={AboutMessage} />
</Switch>
</div>
)
}
}
复制代码
3.7路由跳转
路由跳转可以采用前面学到的<Link></Link>
和<NavLink></NavLink>
,也可以采用js代码跳转,但是这样必须拿到history对象:
- 如果是通过路由跳转过来所渲染的组件,在props里可以拿到history,location,match对象。
- 如果是在组件里面的普通按钮,想点击跳转页面,该怎么做呢?
react-router通过高阶组件让我们可以拿到相关的信息。如果我们希望在组件中获取到history对象,必须满足以下两个条件:
- 组件必须包裹在Router组件之内;
- 组件使用withRouter高阶组件包裹;
3.8参数传递
传递参数有三种方式
- 动态路由的方式;
- search传递参数;
- to传入对象;
3.8.1动态路由的方式
动态路由的方式就是路由参数不是固定的
- 比如/detail路径对应Detail组件
- 如果我们把
<route />
路径写成/detail/:id,那么/detail:123,/detail:abc都会匹配到该route。
当使用动态路由时,我们可以采用this.props.match.params拿到传递的参数。
search传递参数
我们可以在跳转的路径中写query参数
<NavLink to="/detail2?name=why&age=18">详情2</NavLink>
复制代码
使用search传参数时,可以采用this.props.location.search拿到需要的参数
to传入对象传参
<NavLink to={{
pathname: "/detail2",
query: {name: "kobe", age: 30},
state: {height: 1.98, address: "洛杉矶"},
search: "?apikey=123"
}}>
详情2
</NavLink>
复制代码
使用search传参数时,可以采用this.props.location拿到需要的参数