本文介绍如何使用react-router库对单页面应用进行路由配置。
前言
react-router是由React官方维护的路由库,它本身也是一个React组件。通过切换不同的Route
对象来动态加载页面上的components,达到切换页面的效果。编写本文时,react-router已更新至5.2.0版本。使用如下命令进行安装:
npm install --save react-router-dom
基本用法
react-router提供了多种Router对象:<BrowserRouter>
, <HashRouter>
, <MemoryRouter>
, <StaticRouter>
和<NativeRouter>
。我们以官方推荐的BrowserRouter
作为本文的例子。实际实现时一般会使用以上五种高阶Router之一,但原理并无大的差异。它们的的对比将在稍后介绍。
和其他的React元件一样,BrowserRouter
需要在js中引入。使用时需要用其包裹页面的其他组件。例如:
import React from 'react';
import ReactDOM from 'react-dom';
import {BrowserRouter} from 'react-router-dom';
const App = () => {return <h1>Hello World!</h1>}
ReactDOM.render(
<BrowserRouter>
<App />
</BrowserRouter>,
document.getElementById("root")
);
- Link和Route对象
<Route>
对象包含:path
属性,用于匹配URL;component
属性,用来指定URL匹配时需要被渲染的元素;exact
属性用来指定是否严格匹配URL。举例如下:
<BrowserRouter>
<Route exact path="/" component={App} />
<Route path="/index" component={Index}>
</BrowserRouter>
当用户访问根路径时,App
组件将会被加载,当用户访问/index
目录时,IndexPage
将会被加载。
注:如果不在跟路径路由添加exact
属性,则访问/index
路径时App
元件也会被加载。
- Switch对象
React中的<Switch>
和C语言中的switch
用法类似,时它用来切换到第一个匹配的路由分支,其他的路由分支不会被渲染。当多个<Route>
对象被放置在同一层级时,我们使用<Switch>
元件来将其包裹。例如:
<BrowserRouter>
<Switch>
<Route exact path="/" component={App} />
<Route path="/index" component={Index}>
</Switch>
</BrowserRouter>
当使用<Switch>
时,如果我们在第一条路由不指定exact
属性的话,那么无论我们访问"/"
, "/index"
, "/anything"
,Router都将匹配到上段代码的第一条路由,而不渲染其他组件。
- 在Router中使用history
引用官方文档的话:“React Router 是建立在 history 之上的。 简而言之,一个 history 知道如何去监听浏览器地址栏的变化, 并解析这个 URL 转化为 location
对象, 然后 router 使用它匹配到路由,最后正确地渲染对应的组件。“
自react-router v4以来,官方推荐使用的<BrowserRouter>
已经在内部配置好了browserHistory,我们无需进行额外配置。有关History的底层实现,可以参照这篇博文:React路由之BrowserHistory实现原理
- 在其他元件中进行导航
当我们想在其他元件中创建超链接时,只需要使用<Link>
或者<NavLink>
对象即可完成导航。后者是前者的特殊形式,用于在链接被选中时向其添加style样式。它们最终被渲染为HTML中的<a>
标签,只是跳转方式略有不同。例如:
<Link to="/">Back To Home</Link>
<Link to={{
pathname: "/",
search: "/?id=0",
hash: "#hash location",
state: {jumpBack: true}
}}>Back To Home</Link>
如以上代码,to属性接受字符串或者location
类型作为参数。当用户点击链接时,path, search, hash会被合并为href。大致流程时React会调用history.pushState
方法进行URL跳转并将state传入location
对象,然后触发history
中的事件监听器,完成对历史记录的更新。
- 嵌套路由
假设我们的/index
路径下有多个子页面,例如/index/01
, /index/02
等。我们想通过IndexPage内的超链接来访问这两个页面,应当如何在IndexPage
元件里配置子路由?我们现有的index.js如下:
import...
function Index() {
return (
<div>
<ul>
<li> IndexPage01</li>
<li> IndexPage02</li>
</ul>
</div>
);
};
在当前的组件里创建<Router>
,并在<Route>
里写死url固然是一种办法,但这样的写法会增加以后debug和修改的难度。为了避免这种写法,react-router在创建<Route>
元件内所渲染的元素时,会向其props
传入match
参数,这个参数指的就是前一条路由match的路径。由此,我们便可以将当前组件的Router写的更灵活,具体如下:
import...
function Index(props) {
return (
<div>
<ul>
<li> <Link to={props.match.url}/01>IndexPage01</Link></li> <li> <Link to={props.match.url}/02>IndexPage02</Link></li>
</ul>
</div>
<BrowserRouter>
<Route path={props.match.url}/01 component={IndexPage01}/>
<Route path={props.match.url}/02 component={IndexPage02}/>
</BrowserRouter>
);
};
match
中的path
和url
参数也可以通过调用let {path, url} = useRouteMatch()
来获取。