You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
history.listen((location,action)=>{console.log(`The current URL is ${location.pathname}${location.search}${location.hash}`);console.log(`The last navigation action was ${action}`);});
history 对象
首先,一切的基础是window.history,它是一个只读属性,能够返回一个 History 对象。这个对象提供了接口用于操作浏览器回话历史(即 session history)。这一特性是 HTML5 引入的,叫做 history API。
我们首先需要关注 histroy 的两个属性和 5 个方法,通过这些属性和方法,我们得以用代码获取 history 状态或操作 history:
接下来还要关注一个事件:popstate。当 hostory 的当前活动记录发生变化时,popstate 事件会被派发给 window。如果被激活的 history 记录是通过 pushState 创建或者被 replaceSate 方法修改过,则事件有一个 state 字段,其值是对应 history 记录的 state 拷贝。但是需要注意,只有调用back()、forward()、go()方法或者用户点击浏览器的“后退”、“前进”按钮才会产生 popstate 事件,通过 pushState 和 replaceSate 函数调用对 history 的操作并不会产生 popstate 事件。
location
另一的基础是 location,它是一个对象,包含当前页面 URL 的相关信息,并且提供了一些方法来修改 URL。通过 window.loaction 和 document.location 都可以访问该对象(window.loaction === window.loaction)。
location 对象拥有以下属性和方法(TODO:简要说明以下属性和方法):
history 库
history 是一个用于管理 session history 的 JS 库,目前由 ReactTraining 维护。在继续阅读本文之前,最好先去扫一眼 history 库的文档。
history 库的目标是要让你可以在任何能够运行 JS 的地方轻松地管理 session history。那么这里的“任何能够运行 JS 的地方”一般就是指浏览器、Node.js 以及向 React-native 这样的非 DOM 环境。注意:这些环境中只有现代浏览器才支持 HTML5 history API。
它管理 session history 的方法与 HTML5 的 history API 类似,也是提供一个 history 对象。为了在不同的环境中提供相对统一的操作方式,history 库提供了三种创建 history 对象的方法:
history 对象与 HTML5 的 history API 很相似,但功能有所加强,具体区别请看它提供的属性和方法:
属性:
导航方法(请注意方法名与 HTML5 history API 的区别):
监听(这是 HTML5 history API 不提供的):
通过 history.listen 方法可以监听 history.location 的变化,使用方式如下:
通过 listen 方法注册的 listener 函数,无论是用户点击“前进”、“后退”按钮或者代码调用导航方法时,还是代码调用push()、replace()方法时,都会被触发。在前面讲 HTML5 的 history API 时提到,只有调用back()、forward()、go()方法或者用户点击“前进”、“后退”按钮时才会产生 popstate 事件,代码中调用 pushState() 和 replaceState() 方法并不会产生 popstate 事件。所以要实现监听,history 不仅要利用 popstate 事件来监听用户点击“前进”、“后退”按钮的行为,还要自己实现一套监听机制来监听代码中对导航方法的调用。
从 history 源码中发现,它实现了一个 TransitionManager 对象,用于管理监听器以及 prompt(在此可以不用关心 prompt 的管理)。去掉 prompt 管理功能之后的代码如下:
然后再来看 history.listen() 方法的实现:
对这段代码代码做一个简要解释:
首先将 listener 添加到 transitionManager 中去。另外还有对 checkDOMListeners 的函数的调用,这个函数实现的逻辑有点令人不解,而且做法不太科学,存在潜在的问题,在此就不对它进行分析了,只说它的作用。history 代码中实现了一个名为 handlePopState 的函数,用于监听最开始提到的 popstate 事件,checkDOMListeners(1)是为了确保当 transitionManager 对象中注册的 listener 数量不为 0 时该函数被注册 listener 了,checkDOMListeners(-1) 的作用是确保当 transitionManager 对象中注册的 listener 数量为 0 时该函数从事件监听上移除。
再来看 handlePopState 函数:
该函数最后调用了名为 handlePop 的函数,再继续看这个函数:
先分析这个函数的作用:经过 confirmTransitionTo 抉择(这属于支线剧情,暂时不关心)之后,最终调用 setState 函数,setState 函数的实现如下:
在这个函数里,我们通过 history.listen 注册的所有监听器终于被触发了。这就说明,popstate 事件的监听函数,最终会调用我们通过 history.listen 注册的所有监听器,也就是说 history.listen() 达到了监听 popstate 事件的效果。
至此,已经清楚 history 如何利用 popstate 事件来监听用户点击“前进”、“后退”按钮或者代码里的导航动作,接下来继续探究它如何监听用户代码对 push 和 replace 的调用。
通过寻找 setState 函数被调用的地方发现,history 对象的 push 和 replace 函数体中都调用了 setState,那这就很明了了:调用 push 和 replace 也能触发通过 history.listen 注册的所有监听器。
接下来分析其中的细节。
loaction:
从 createBrowserHistory() 函数返回的 history 对象中,location 属性包含了最多的信息,现在来看看 location 对象里有些什么数据。文档中已经有说明:
createMemoryHistory)
如果是在支持 HTML5 story API 的现代浏览器环境下,location 中的字段其实是将 window.history.state 和 window.location 这两个对象的数据合并之后的结果。
TODO:待续
The text was updated successfully, but these errors were encountered: