视频1 视频21 视频41 视频61 视频文章1 视频文章21 视频文章41 视频文章61 推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37 推荐39 推荐41 推荐43 推荐45 推荐47 推荐49 关键词1 关键词101 关键词201 关键词301 关键词401 关键词501 关键词601 关键词701 关键词801 关键词901 关键词1001 关键词1101 关键词1201 关键词1301 关键词1401 关键词1501 关键词1601 关键词1701 关键词1801 关键词1901 视频扩展1 视频扩展6 视频扩展11 视频扩展16 文章1 文章201 文章401 文章601 文章801 文章1001 资讯1 资讯501 资讯1001 资讯1501 标签1 标签501 标签1001 关键词1 关键词501 关键词1001 关键词1501 专题2001
ReactRouter中的核心history库的详细分析
2020-11-27 15:04:36 责编:小采
文档

这篇文章给大家介绍的内容是关于React Router中的核心history库的详细分析,有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。

前言

使用React开发稍微复杂一点的应用,React Router几乎是路由管理的唯一选择。虽然React Router经历了4个大版本的更新,功能也越来越丰富,但无论怎么变,它的核心依赖history库却一直没变。下面我们来了解下这个在github上有4k+星的库到底提供了什么功能。

HTML5 history对象

聊到history库,是不是觉得这个单词有点熟悉?不错,HTML5规范里面,也新增了一个同名的history对象。下面我们来看下这个history对象用来解决什么问题。

在jQuery统治前端的年代,通过ajax请求无刷新更新页面是当时相当流行的页面处理方式,SPA的雏形就是那时候演化出来的。为了标示页面发生的变化,方便刷新后依然能显示正确的页面元素,一般会通过改变url的hash值来唯一定位页面。但这会带来另一个问题:用户无法使用前进/后退来切换页面。

为了解决这个问题,history对象应运而生。当页面的url或者hash发生变化的时候,浏览器会自动将新的url push到history对象中。history对象内部会维护一个state数组,记录url的变化。在浏览器进行前进/后退操作的时候,实际上就是调用history对象的对应方法(forward/back),取出对应的state,从而进行页面的切换。

除了操作url,history对象还提供2个不用通过操作url也能更新内部state的方法,分别是pushStatereplaceState。还能将额外的数据存到state中,然后在onpopstate事件中再通过event.state取出来。如果希望对history对象作更深入的理解,可以参考 这里,和这里。

history库与HTML5 history对象的关系

我们再回过头来看history库。它本质上做了以下4件事情:

  1. 借鉴HTML5 history对象的理念,在其基础上又扩展了一些功能

  2. 提供3种类型的history:browserHistory,hashHistory,memoryHistory,并保持统一的api

  3. 支持发布/订阅功能,当history发生改变的时候,可以自动触发订阅的函数

  4. 提供跳转拦截、跳转确认和basename等实用功能

再对比一些两者api的异同。以下是history库的:

const history = {
 length, // 属性,history中记录的state的数量
 action, // 属性,当前导航的action类型
 location, // 属性,location对象,封装了pathname、search和hash等属性
 push, // 方法,导航到新的路由,并记录在history中
 replace, // 方法,替换掉当前记录在history中的路由信息
 go, // 方法,前进或后退n个记录
 goBack, // 方法,后退
 goForward, // 方法,前进
 canGo, // 方法,是否能前进或后退n个记录
 block, // 方法,跳转前让用户确定是否要跳转
 listen // 方法,订阅history变更事件
 };

以下是HTML5 history对象的:

const history = {
 length, // 属性,history中记录的state的数量
 state, // 属性,pushState和replaceState时传入的对象
 back, // 方法,后退
 forward, // 方法,前进
 go, // 方法,前进或后退n个记录
 pushState, // 方法,导航到新的路由,并记录在history中
 replaceState // 方法,替换掉当前记录在history中的路由信息
}

// 订阅history变更事件
window.onpopstate = function (event) {
 ...
}

从对比中可以看出,两者的关系是非常密切的,history库可以说是history对象的超集,是功能更强大的history对象。

createHashHistory源码分析

下面,我们以三种history类型中的一种,hashHistory为例,来分析下history的源码,看看它都干了些什么。先看下它是怎么处理hash变更的。

// 构造hashHistory对象
const createHashHistory = (props = {}) => {
 ...
 const globalHistory = window.history; // 引用HTML5 history对象
 ...
 // transitionManager负责控制是否进行跳转,以及跳转后要通知到的订阅者,后面会详细讨论
 const transitionManager = createTransitionManager();
 ...
 // 注册history变更回调的订阅者
 const listen = listener => {
 const unlisten = transitionManager.appendListener(listener);
 checkDOMListeners(1);

 return () => {
 checkDOMListeners(-1);
 unlisten();
 };
 };
 
 // 监听hashchange事件
 const checkDOMListeners = delta => {
 listenerCount += delta;

 if (listenerCount === 1) {
 window.addEventListener(HashChangeEvent, handleHashChange);
 } else if (listenerCount === 0) {
 window.removeEventListener(HashChangeEvent, handleHashChange);
 }
 };
 
 // hashchange事件回调
 const handleHashChange = () => {
 ...
 // 构造内部使用的location对象,包含pathname、search和hash等属性
 const location = getDOMLocation(); 
 ...
 handlePop(location);
 };
 
 // 处理hash变更逻辑
 const handlePop = location => {
 ...
 const action = "POP";
 // 给用户展示确认跳转的信息(如果有的话),确认后通知订阅者。如果用户取消跳转,则回退到之前状态
 transitionManager.confirmTransitionTo(location, action, getUserConfirmation, ok => {
 if (ok) {
 setState({action, location}); // 确认后通知订阅者
 } else {
 revertPop(location); // 取消则回退到之前状态
 }
 });
 };
 
 // 更新action,location和length属性,并通知订阅者
 const setState = nextState => {
 Object.assign(history, nextState);

 history.length = globalHistory.length;

 transitionManager.notifyListeners(history.location, history.action);
 };
 ...
}

下载本文
显示全文
专题