从 React Router 谈谈路由的那些事

React Router是专为 React 设计的路由解决方案,在使用 React 来开发 SPA (单页应用)项目时,都会需要路由功能,而 React Router 应该是目前使用率最高的。

React Router 并不是 Facebook 的 React 官方团队开发的,但是据说有官方人员参与开发。React Router 的设计思想来源于 Ember 的路由,如果原来有用过 Ember 的路由,那么应该对 React Router 不会陌生。

什么是路由?

对于没有开发过后端,也没有开发过 SPA 的前端来说,「路由」这个名词可能会让人比较困惑,这里的路由并不是指「硬件路由」,也不是网络七层协议中的「网络层路由」,但是其思想原理是一样的。我尽量简单通俗的介绍一下。

假如我们有一台提供 Web 服务的服务器的网络地址是:10.0.0.1,而该 Web 服务又提供了三个可供用户访问的页面,其页面 URI 分别是:

1 http://10.0.0.1/
2 //10.0.0.1/about
3 //10.0.0.1/concat

那么其路径就分别是//about/concat

用户使用http://10.0.0.1/about来访问该页面时,Web 服务会接收到这个请求,然后会解析 URI 中的路径/about,在 Web 服务的程序中,该路径对应着相应的处理逻辑,程序会把请求交给路径所对应的处理逻辑,这样就完成了一次「路由分发」,这个分发就是通过「路由」来完成的。

前端路由

前端的路由和后端的路由在实现技术上不一样,但是原理都是一样的。在 HTML5 的historyAPI 出现之前,前端的路由都是通过hash来实现的,hash能兼容低版本的浏览器。如果我们把上面例子中提到的 3 个页面hash来实现的话,它的 URI 规则中需要带上#

//10.0.0.1/#/about
//10.0.0.1/#/concat

Web 服务并不会解析hash,也就是说#后的内容 Web 服务都会自动忽略,但是 JavaScript 是可以通过window.location.hash读取到的,读取到路径加以解析之后就可以响应不同路径的逻辑处理。

history是 HTML5 才有的新 API,可以用来操作浏览器的 session history (会话历史)。基于history来实现的路由可以和最初的例子中提到的路径规则一样。

用户可能都察觉不到该访问地址是 Web 服务实现的路由还是前端实现的路由。

性能用户体验的层面来比较的话,后端路由每次访问一个页面的时候都要向服务器发送请求,然后服务器再响应请求,这个过程肯定会有延迟。而前端路由在访问一个页面的时候仅仅是变换了一下路径而已,没有了网络延迟,对于用户体验来说会有相当大的提升。

说了这么多的「路由基础」,该回头来说说 React Router 了。

配置路由

使用 React Router 来配置上面例子中的三个页面,每个页面分别对应着一个 React Component。

/about页面的入口文件about.js

01 import React from'react';
02
03 classAbout extends React.Component {
04 render () {
05 return(
06 <h3>ThisisAbout page.</h3>
07 );
08 }
09 };
10 11 exportdefaultAbout;

/concat页面的入口文件concat.js

constConcat = () => {
4 (
5 Concat page.</h3>
6 );
7 8 9 Concat;

/首页对应的是app.js,它也是整个 React Component 的入口文件

import {Link} from'react-router';

App extends React.Component {
render () {
<div>
<h1>React Router Demo</h1>
<hr />
<p>
by <a href="http://stylechen.com/"target="_blank">stylechen.com</a>
12 </p>
13 <ul>
14 <li><Link to="/">Home</Link></li>
15 <li><Link to="/about">About</Link></li>
16 <li><Link to="/concat">Concat</Link></li>
17 </ul>
18 {this.props.children}
19 </div>
20 )
21 }
22 };
23 24 App;

app.js 中的Link组件是 React Router 提供的组件,用于链接到相应页面。如果直接使用 a 标签的话相当于页面跳转了,而使用Link只是应用内的路由跳转页面跳转意味着先重新加载整个页面,然后才是应用内部的路由跳转

index.js中包含了 route 的配置,同时在该文件中对 React Component 进行render

import ReactDOM from'react-dom'import App from'./component/app'import About from'./component/about'import Concat from'./component/concat'import {Router,Route,browserHistory} from'react-router'router = (
<Router history={browserHistory}>
<Route path="/"component={App}>
<Route path="about"component={About} />
<Route path="concat"component={Concat} />
</Route>
</Router>
);
ReactDOM.render(
router,
document.getElementById('root')
);

Route组件就是用于配置路由,path属性用于配置路径,component就是对应的 React Component。

Route组件支持嵌套,嵌套时子组件的路径可以继承父组件的路径,上面的about嵌套后就成了/about,当然也可以直接以根路径为开头。

此时再看看 app.js 中的this.props.childrenAboutConcat两个页面组件其实就是以这种形式插入到父组件中,只是插入的时候用 React Router 提供的组件再包装了一下使它们可以支持路由。

Router组件还有一个重要的属性,那就是history,这可以配置使用history来实现路由,如果没有配置这个属性认使用hash

路由参数

假如我们有很多 list 页面,这些页面除了动态内容不同,其他的页面部分都相同,这个时候需要怎么配置路由和组件呢?

这种场景就需要用到路由的参数功能增加一条包含参数的路由配置。

import List from'./component/list'<Route path="list/:id"component={List} />

注意path属性中的:id就是该路由的参数(param)。再来看看List页面的组件。

/list对应了list.js

List extends React.Component {
<div>
List page.</h3>
<p>The list page id&nbsp;
<b style={{color:'red'}}>{.props.params.id}</b>
List;

List组件中,可以直接通过this.props.params.id来访问实际的参数值(这里的id key 就和定义路径的:id相对应),React Router 将路由的数据都通过props传递给了页面组件,这样就可以非常方便的访问路由相关的数据了。

组件的生命周期

每个 React Component 都有生命周期,按照常规的策略,当调用父组件的render的时候,会将所有的页面子组件也进行render,这种逻辑显然不合理了。那么当一个 React 的应用有了路由功能后,它的生命周期会如何处理呢?

当切换路由的路径的时候才去 render 对应的页面组件,给About绑定两个组件装载和卸载的事件,就可以测试出来了。

componentDidMount () {
console.log('mount'componentwillUnmount () {
console.log('un mount' 在切换到/about页面的时候,会打印出mount,而当离开页面的时候会打印出un mount。路由的这种进入时装载组件离开时卸载组件的策略就可以做到合理利用「资源」,不会一下把所有的组件都装载进来使内存占用飙升,也不会离开时没有卸载而时内存泄漏。

React Router 还有很多比较方便 API,这里不一一列举,通过上面的文章基本能覆盖到常规的使用场景了,想了解更多使用方面查看项目主页

本文的 demo 代码都已经托管在github上了,可以自己下载到本地浏览。

原载于:雨夜带刀’s Blog
本文链接http://stylechen.com/react-router.html 如需转载请以链接形式注明原载或原文地址。

相关文章

一、前言 在组件方面react和Vue一样的,核心思想玩的就是组件...
前言: 前段时间学习完react后,刚好就接到公司一个react项目...
前言: 最近收到组长通知我们项目组后面新开的项目准备统一技...
react 中的高阶组件主要是对于 hooks 之前的类组件来说的,如...
我们上一节了解了组件的更新机制,但是只是停留在表层上,例...
我们上一节了解了 react 的虚拟 dom 的格式,如何把虚拟 dom...