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 分别是:

 
http://10.0.0.1/
http://10.0.0.1/about
http://10.0.0.1/concat
 

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

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

前端路由

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

http://10.0.0.1/
http://10.0.0.1/#/about
http://10.0.0.1/#/concat
 

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

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

http://10.0.0.1/
http://10.0.0.1/about
http://10.0.0.1/concat
 

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

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

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

配置路由

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

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

import React from 'react';

class About extends React.Component {
componentDidMount () {
console.log('mount');
}

componentWillUnmount () {
console.log('un mount');
}

render () {
return (
<h3>This is About page.</h3>
);
}
};

export default About;

 

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

 

import React from 'react';

const Concat = () => {
return (
<h3>This is Concat page.</h3>
);
};

export default Concat;

 

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

 

import React from 'react';
import {Link} from 'react-router';

class App extends React.Component {
render () {
return (
<div>
<h1>React Router Demo</h1>
<hr />
<p>
by <a href="http://stylechen.com/" target="_blank">stylechen.com</a>
</p>
<ul>
<li><Link to="/">Home</Link></li>
<li><Link to="/about">About</Link></li>
<li><Link to="/concat">Concat</Link></li>
<li><Link to="/list/001">List 001</Link></li>
<li><Link to="/list/002">List 002</Link></li>
</ul>
{this.props.children}
</div>
)
}
};

export default App;

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

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

import React from 'react';
import ReactDOM from 'react-dom';

import App from './component/app';
import About from './component/about';
import Concat from './component/concat';
import List from './component/list';

import {Router, Route, browserHistory} from 'react-router';

const router = (
<Router history={browserHistory}>
<Route path="/" component={App}>
<Route path="about" component={About} />
<Route path="concat" component={Concat} />
<Route path="list/:id" component={List} />
</Route>
</Router>
);

ReactDOM.render(
router,
document.getElementById('root')
);

 

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

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

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

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

路由参数

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

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

 
import List from './component/list';
 

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

/list 对应了 list.js

import React from 'react';

class List extends React.Component {
render () {
return (
<div>
<h3>This is List page.</h3>
<p>The list page id is&nbsp;
<b style={{color: 'red'}}>{this.props.params.id}</b>
</p>
</div>
);
}
};

export default List;

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

组件的生命周期

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

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

importReactfrom'react';

 
classAboutextendsReact.Component{
    componentDidMount(){
        console.log('mount');
    }
 
    componentWillUnmount(){
        console.log('un mount');
    }
 
    render(){
        return(
            ThisisAboutpage.
        );
    }
};
exportdefaultAbout;
 

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

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

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

从 React Router 谈谈路由的那些事的更多相关文章

  1. React Router v4 页面传值的三种方法

    传值方法 1.props.params 使用React router定义路由时,我们可以给指定一个path,然后指定通配符可以携带参数到指定的path: <Route path='/user/: ...

  2. React 实践记录 03 React router

    Introduction 本文主要参考了react router 的官方文档. React Router是一套完整的配合React的路由解决方案,可能你已经知道前端路由,或者知道后端有路由的概念,如下 ...

  3. react router 4.0以上的路由应用

    thead>tr>th{padding:8px;line-height:1.4285714;border-top:1px solid #ddd}.table>thead>tr& ...

  4. react router @4 和 vue路由 详解(七)react路由守卫

    完整版:https://www.cnblogs.com/yangyangxxb/p/10066650.html 12.react路由守卫? a.在之前的版本中,React Router 也提供了类似的 ...

  5. react router @4 和 vue路由 详解(全)

    react router @4 和 vue路由 本文大纲: 1.vue路由基础和使用 2.react-router @4用法 3.什么是包容性路由?什么是排他性路由? 4.react路由有两个重要的属 ...

  6. React Router 4.0 实现路由守卫

    在使用 Vue 或者 Angular 的时候,框架提供了路由守卫功能,用来在进入某个路有前进行一些校验工作,如果校验失败,就跳转到 404 或者登陆页面,比如 Vue 中的 beforeEnter 函 ...

  7. React初识整理(四)--React Router(路由)

    官网:https://reacttraining.com/react-router 后端路由:主要做路径和方法的匹配,从而从后台获取相应的数据 前端路由:用于路径和组件的匹配,从而实现组件的切换. 如 ...

  8. < react router>: (路由)

    < react router> (路由): 思维导图: Atrial   文件夹下的index.js 文件内容: import React, { Component } from 'rea ...

  9. react router @4 和 vue路由 详解(五)react怎么通过路由传参

    完整版:https://www.cnblogs.com/yangyangxxb/p/10066650.html 7.react怎么通过路由传参? a.通配符传参(刷新页面数据不丢失) //在定义路由的 ...

随机推荐

  1. Java AES加密解密工具 -- GUI 、在线传输文件

    原理 对于任意长度的明文,AES首先对其进行分组,每组的长度为128位.分组之后将分别对每个128位的明文分组进行加密. 对于每个128位长度的明文分组的加密过程如下:     (1)将128位AES ...

  2. C# WPF 粘贴板记录器

    工作学习中需要搜索很多资料,有建立文档对遇到过的问题进行记录,但是一来麻烦,二来有些当时认为不重要的事情,也许一段时间后认为是重要的,需要记录的,却又一时找不到,浪费时间做重复的事情.正好借着这个机会 ...

  3. python入门:输出1-10的所有数

    #!/usr/bin/env python # -*- coding:utf-8 -*- #输出1-10的所有数 """ 变量kaishi的赋值为数字1,while 真, ...

  4. 【android】安卓开发apk列表

    - 谷歌的Zxing框架的扫码软件 (目前国内的应用商店很少此种类型的扫码app) - 解析IP地址功能,从IP地址(子网掩码)自动解析出网段,广播地址

  5. 中移物联网onenet入门学习笔记1:资料获取

    onenet学习资料.视频.例程汇总:https://open.iot.10086.cn/bbs/thread-977-1-1.html onenet开发文档:https://open.iot.100 ...

  6. MongoDB之Replica Sets环境搭建

    最近学习MongoDB,这两天在搭建复制集的时候碰到了不少问题,也踩了好多坑,现在在这里记录下来,以供自己和他人参考 (因为本人是初学者,所以遇到的问题也会比较初级,所以本文也比较适合初学者查阅) 背 ...

  7. 两种图片延迟加载的方法总结jquery.scrollLoading.js与jquery.lazyload.js

    估计网上能查到的最多的两种图片延迟加载方法就是jquery.scrollLoading.js与jquery.lazyload.js了,其中jquery.lazyload.js的调用方法因为有网友爆出的 ...

  8. df和du显示的磁盘空间使用情况不一致问题

    背景介绍: dba同事删除了mysql /datao目录下的文件,通过du –sh查看空间使用700G,df -h查看空间使用1T,没有重启mysql服务. 另一个表现出du与df命令不同之处的例子如 ...

  9. 让读者快速了解RocketMQ消息中间件需要解决哪些问题

    本文首先引出消息中间件通常需要解决哪些问题,在解决这些问题当中会遇到什么困难,Apache RocketMQ作为阿里开源的一款高性能.高吞吐量的分布式消息中间件否可以解决,规范中如何定义这些问题.然后 ...

  10. String、StringBuffer和StringBuilder,定义一个自己的StringBuilder的类

    String Java中的字符串值属于String类,虽然有其它方法表示字符串(如字符数组),但Java一般使用String类作为字符串的标准格式,Java编译器把字符串值作为String对象; St ...