文档章节

react-router中history

hqxluoyang
 hqxluoyang
发布于 2016/05/17 15:02
字数 1323
阅读 290
收藏 2

目前, react 的生态越来越丰富,像 flux redux react-router 已经被越来越多的使用,本文就 react-router 的内部实现进行分析。文章主要包含两大部分: 一是对 react-router 赖以依存的 history 进行研究;二是分析 react-router 是如何实现URL 与 UI 同步的。

1. react-router的依赖基础 – history

1.1 History的整体介绍

history 是一个独立的第三方js库,可以用来兼容在不同浏览器、不同环境下对历史记录的管理,拥有统一的API。具体来说里面的history分为三类:

  • 老浏览器的history: 主要通过hash来实现,对应 createHashHistory
  • 高版本浏览器: 通过html5里面的history,对应 createBrowserHistory
  • node环境下: 主要存储在memeory里面,对应 createMemoryHistory

上面针对不同的环境提供了三个API,但是三个API有一些共性的操作,将其抽象了一个公共的文件 createHistory :

// 内部的抽象实现 function createHistory(options={}) {   ...   return {     listenBefore, // 内部的hook机制,可以在location发生变化前执行某些行为,AOP的实现     listen, // location发生改变时触发回调     transitionTo, // 执行location的改变     push, // 改变location     replace,     go,     goBack,     goForward,     createKey, // 创建location的key,用于唯一标示该location,是随机生成的     createPath,     createHref,     createLocation, // 创建location   } } 

 

上述这些方式是history内部最基础的方法, createHashHistory 、 createBrowserHistory 、 createMemoryHistory 只是覆盖其中的某些方法而已。其中需要注意的是,此时的location跟浏览器原生的location是不相同的,最大的区别就在于里面多了key 字段, history 内部通过 key 来进行 location 的操作。

function createLocation() {   return {     pathname, // url的基本路径     search, // 查询字段     hash, // url中的hash值     state, // url对应的state字段     action, // 分为 push、replace、pop三种     key // 生成方法为: Math.random().toString(36).substr(2, length)   } } 

 

1.2 内部解析

三个API的大致的技术实现如下:

  • createBrowserHistory : 利用HTML5里面的history
  • createHashHistory : 通过hash来存储在不同状态下的history信息
  • createMemoryHistory : 在内存中进行历史记录的存储

1.2.1 执行URL前进

  • createBrowserHistory : pushState、replaceState
  • createHashHistory : location.hash=*** location.replace()
  • createMemoryHistory : 在内存中进行历史记录的存储

代码实现如下:

// createBrowserHistory(HTML5)中的前进实现 function finishTransition(location) {   ...   const historyState = { key };   ...   if (location.action === 'PUSH') ) {     window.history.pushState(historyState, null, path);   } else {     window.history.replaceState(historyState, null, path)   } } // createHashHistory的内部实现 function finishTransition(location) {   ...   if (location.action === 'PUSH') ) {     window.location.hash = path;   } else {     window.location.replace(     window.location.pathname + window.location.search + '#' + path   );   } } // createMemoryHistory的内部实现 entries = []; function finishTransition(location) {   ...   switch (location.action) {     case 'PUSH':       entries.push(location);       break;     case 'REPLACE':       entries[current] = location;       break;   } } 

 

1.2.2 检测URL回退

  • createBrowserHistory : popstate
  • createHashHistory : hashchange
  • createMemoryHistory : 因为是在内存中操作,跟浏览器没有关系,不涉及UI层面的事情,所以可以直接进行历史信息的回退

伪代码实现如下:

// createBrowserHistory(HTML5)中的后退检测 function startPopStateListener({ transitionTo }) {   function popStateListener(event) {     ...     transitionTo( getCurrentLocation(event.state) );   }   addEventListener(window, 'popstate', popStateListener);   ... }  // createHashHistory的后退检测 function startPopStateListener({ transitionTo }) {   function hashChangeListener(event) {     ...     transitionTo( getCurrentLocation(event.state) );   }   addEventListener(window, 'hashchange', hashChangeListener);   ... } // createMemoryHistory的内部实现 function go(n) {   if (n) {     ...     current += n;   const currentLocation = getCurrentLocation();   // change action to POP   history.transitionTo({ ...currentLocation, action: POP });   } } 

 

1.2.3 state的存储

为了维护state的状态,将其存储在sessionStorage里面:

// createBrowserHistory/createHashHistory中state的存储 function saveState(key, state) {   ...   window.sessionStorage.setItem(createKey(key), JSON.stringify(state)); } function readState(key) {   ...   json = window.sessionStorage.getItem(createKey(key));   return JSON.parse(json); } // createMemoryHistory仅仅在内存中,所以操作比较简单 const storage = createStateStorage(entries); // storage = {entry.key: entry.state}  function saveState(key, state) {   storage[key] = state } function readState(key) {   return storage[key] } 

 

2. react-router的基本原理

一句话:实现URL与UI界面的同步。其中在react-router中, URL 对应 Location 对象,而UI是由react components 来决定的,这样就转变成 location 与 components 之间的同步问题。

react-router的实现原理

3. react-router的具体实现

react-router在 history 库的基础上,实现了URL与UI的同步,分为两个层次来描述具体的实现。

3.1 组件层面描述具体实现过程

在 react-router 中最主要的 component 是 Router RouterContext Link , history 库起到了中间桥梁的作用。

react-router的实现原理

3.2 API层面描述具体实现过程

为了简单说明,只描述使用browserHistory的实现,hashHistory的实现过程是类似的,就不在说明。

react-router的实现原理

4. 结语

目前 react-router 在项目中已有大量实践,其优点可以总结如下:

  • 风格: 与React融为一体,专为react量身打造,编码风格与react保持一致,例如路由的配置可以通过component来实现
  • 简单: 不需要手工维护路由state,使代码变得简单
  • 强大: 强大的路由管理机制,体现在如下方面
    • 路由配置: 可以通过组件、配置对象来进行路由的配置
    • 路由切换: 可以通过 <Link> Redirect 进行路由的切换
    • 路由加载: 可以同步记载,也可以异步加载,这样就可以实现按需加载
  • 使用方式: 不仅可以在浏览器端的使用,而且可以在服务器端的使用

当然 react-router 的缺点就是API不太稳定,在升级版本的时候需要进行代码变动。

 

© 著作权归作者所有

上一篇: 关于 css中box属性
下一篇: react-router
hqxluoyang
粉丝 8
博文 79
码字总数 19390
作品 0
北京
前端工程师
私信 提问
React-Router底层原理分析与实现

用技术改变生活,用生活完善技术。来自微播易一枚向全栈方向努力奔跑的前端工程师。 微信同步:wDxKn89 React-Router基本了解 对于React-Router是针对React定义的路由库,用于将URL和compone...

北宸南蓁
2018/08/20
0
0
react-router4入门教程

npm包的区别 首先从react-router的npm包开始看起,分别有react-router和react-router-dom,两者的区别在于后者具有一些dom api,比如Link、BrowserRouter、HashRouter。不难发现react-route...

zwmmm
02/15
0
0
学习React-router 4.x版本

这几天闲下来想开发我个人网站的极客教程的移动web版本,然后发现之前弄的开发移动web的环境过于老旧,于是重新弄了个结合现在最新的相关库的环境,相关文章可以看搭建我的网站的mobile版的开...

极客教程
2017/10/20
0
0
3. react-router-dom源码揭秘 - BrowserRouter

今天开始,我们开始揭开react-router-dom神秘的头盖骨,哦不,面纱。 在此之前,我们需要了解一些预备知识:React的context和react-router-dom的基本使用。需要复习的同学请移步: 第一篇 Co...

phgap
2018/11/12
0
0
实现一个 react-router

本文将用尽可能容易理解的方式,实现最小可用的 react-router v4 和 history,目的为了了解 react-router 实现原理。 本文首发于 daweilv.com 一、开始之前 在开始阅读本文之前,希望你至少使...

lnyx
09/13
0
0

没有更多内容

加载失败,请刷新页面

加载更多

堆”和“栈

C++作为一款C语言的升级版本,具有非常强大的功能。它不但能够支持各种程序设计风格,而且还具有C语言的所有功能。我们在这里为大家介绍的是其中一个比较重要的内容,C++内存区域的基本介绍。...

SibylY
30分钟前
3
0
总结:Https

一、介绍 简单理解,https即在http协议的基础上,增加了SSL协议,保障数据传输的安全性。 它由以前的http—–>tcp,改为http——>SSL—–>tcp;https采用了共享密钥加密+公开密钥加密的方式 ...

浮躁的码农
32分钟前
4
0
数据库表与表之间的一对一、一对多、多对多关系

表1 foreign key 表2 多对一:表 1 的多条记录对应表 2 的一条记录 利用foreign key的原理我们可以制作两张表的多对多,一对一关系 多对多: 表1的多条记录可以对应表2的一条记录 表2的多条记...

Garphy
今天
7
0
MySQL 表崩溃修复

MySQL日志报错 2019-10-19 13:41:51 19916 [ERROR] /usr/local/mysql/bin/mysqld: Table './initread_hss/user_info' is marked as crashed and should be repaired2019-10-19 13:41:51 1......

雁南飞丶
今天
6
0
Error和Exception

1.Error类和Exception类都是继承Throwable类 2.Error(错误)是系统中的错误,程序员是不能改变的和处理的,是在程序编译时出现的错误,只能通过修改程序才能修正。一般是指与虚拟机相关的问...

大瑞清_liurq
今天
4
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部