React Router 基础入门

66 min read

组成

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