组成
React Router包由三部分组成:
react-router:包含了React Router的大多数核心功能,包括路由匹配算法和大多数核心组件以及hooks。
react-router-dom:除了react-router的内容之外,还添加了一些DOM相关的API,如<BrowserRouter>
, <HashRouter>
, <Link>
等。
安装
yarn add react-router-dom
当安装react-router-dom 和 react-router-native时,会自动包含react-router作为依赖,并且还会自动导出react-router里的所有东西。所以使用时只需要安装react-router-dom或react-router-native即可,不要直接使用react-router。
基础路由
import React from "react";
import {
BrowserRouter as Router,
Switch,
Route,
Link
} from "react-router-dom";
export default function App() {
return (
<Router>
<div>
<nav>
<ul>
<li>
<Link to="/">Home</Link>
</li>
<li>
<Link to="/about">About</Link>
</li>
<li>
<Link to="/users">Users</Link>
</li>
</ul>
</nav>
{/* <Switch>会遍历所有子<Route>,然后渲染第一个成功匹配到当前URL的 */}
<Switch>
<Route path="/about">
<About />
</Route>
<Route path="/users">
<Users />
</Route>
<Route path="/">
<Home />
</Route>
</Switch>
</div>
</Router>
);
}
function Home() {
return <h2>Home</h2>;
}
function About() {
return <h2>About</h2>;
}
function Users() {
return <h2>Users</h2>;
}
-
Router 路由器, 用于管理路由
-
Route 路由,用于映射路径和组件间的关系
-
Router 必须包含 Router router Link 等组件
基础组件
React Router中包含三种基础类型的组件:
路由器(routers), 如 <BrowserRouter>
和 <HashRouter>
路由匹配组件(route matchers), 如 <Route>
和 <Switch>
路由导航组件(navigation/route changers), 如 <Link>
, <NavLink>
, 和 <Redirect>
BrowserRouter 与 HashRouter
BrowserRouter 需要服务器设置,不支持IE9以下
HashRouter 不需要服务器设置
Switch 和 Route
有两个路由匹配组件:Switch 和 Route。当渲染一个<Switch>
时,它会遍历所有子组件,也就是<Route>
组件,找到第一个path匹配当前URL的组件就会停止查找,然后渲染这个<Route>
组件,忽略其他所有的。因此你应该把路径更具体的路由组件放在前面。
如果没有<Route>
成功匹配,<Switch>
就什么都不渲染(null)。
精准匹配和模糊匹配
一个<Route path>
总是匹配URL的开头,而不是整个URL,因此<Route path="/">
总是会匹配URL。鉴于此,通常会将<Route path="/">
放在<Switch>
的最后。可以使用<Route exact path="/">
来匹配完整URL。
定义链接激活样式
React Router提供一个<Link>
组件来创建导航链接。当渲染一个<Link>
时,实际上会在HTML中渲染一个<a>
标签。
<NavLink>
是一个特殊的<Link>
,只不过可以定义激活时的样式。一般通过自定义组件来使用NavLink
兜底的重定向路由组件
任何时候你想使用强制导航,都可以渲染一个<Redirect>
。当一个<Redirect>
被渲染时,将会导航到它的to属性定义的位置:
<Redirect to="/login" />
嵌套路由
许多路由器都有一些“嵌套路由”的概念。React Router直到v4版本也有这个概念。当你从一个静态路由配置迁移到动态路由,你怎么“嵌套路由”?很简单,由于Route仅仅是一个组件,所以你可以像嵌套div一样做就可以了:
const App = () => (
<BrowserRouter>
{/* here's a div */}
<div>
{/* here's a Route */}
<Route path="/tacos" component={Tacos} />
</div>
</BrowserRouter>
);
// 当url 匹配 `/tacos` 时这个组件将会渲染
const Tacos = ({ match }) => (
// 这里是一个嵌套的div
<div>
{/* 这里是一个嵌套的 Route,
match.url 帮助我们生成一个相对路径 */}
<Route path={match.url + "/carnitas"} component={Carnitas} />
</div>
);
编程式导航
this.props.history.push(’/user’,{info:123}),第一个参数path,第二个是state(类似vue路由的元信息)
this.props.history.replace(path,state)
this.props.history.go(n)等同vuerouter
this.props.history.goBack()浏览器回退一步
this.props.history.forForward()浏览器前进一步
监听路由变化
// App.jsx
function App(){
<BrowserRouter>
<RouterGuard></RouterGuard>
</BrowserRouter>
}
// RouterGuard.jsx
import {withRouter,Route} from "react-router"
class RouterGuard extends Component{
whilteList=['/login','/home']
componentDidMount() {
this.unlisten = this.props.histroy.listen((location,action) => {
//location是路由对象,action是操作路由栈的方式
let { pathname } = location;
//访问路径不在白名单内并且没有登录
if(!this.whiteList.includes(pathname)&&!isLogin){
this.props.history.replace('/login')
}
});
}
// 组件卸载时,解除监听
componentWillUnmount() {
this.unlisten();
}
render(){
return (
<>
// 在这里写路由
<Route path='/home' component={home}></Route>
...
// 也可使用下面将使用的方法getRoue()
</>
)
}
}
export default withRouter(RouterGuard)
React 路由配置
// routerConfig.js
import Login from './view/Login';
import Home from './view/Home';
import Root from './view/Root';
let routes = [
{
path: '/',
component: Root,
children: [
{
path: '/login',
component: Login,
},
{
path: '/home',
component: Home,
},
],
},
];
// 将路由path转化成绝对路径
routes.forEach(item => {
if (item.children) {
item.children.map(child => {
if (!child.path.startsWith('/')) {
child.path = item.path + child.path;
}
});
}
});
export default routes
在写个组件遍历routes,返回
function getRoute(routes){
//1. 判断routes是不是array
if(!Array.isArray(routes)){
return null;
}
// 遍历routes
let routesDom = routes.map((item,index)=>{
// 解构出item中的children,递归处理children
let {children,component:Component,render,..rest} = item;
return <Route
key={index}
{...rest}
render={value=>{
// value是[history,location,match]等信息
<Component {...value}>
//递归处理children
{getRoute(children)}
</Component>
}}/>
})
return <Switch>//switch组件保证只匹配一个路由(从上到下)
{routesDom}
</Switch>
}
export default withRouter(getRoute)
使用
function App(){
return (
<BrowserRouter>
<getRoute></getRoute>
</BrowserRouter>
)
}
demo
https://codesandbox.io/s/react-router-demo-r59jg?fontsize=14&hidenavigation=1&theme=dark&file=/src/Login.js