专栏首页OECOM单页面应用history路由实现原理

单页面应用history路由实现原理

在单页面应用中history路由是很受欢迎的,它的路由显示方式和传统的路由方式相同,在显示上很美观,比hash的方式看着舒服的多。我们经常使用的api比如push或pushState,replace或replaceState,go,forward,back等等,其实都是和html5内置的history对象息息相关的,其原理就是调用了HTML5的history内置对象,然后进行了一些封装操作。

history对象属性

window 对象通过 history 对象提供了对浏览器的回话历史的访问(不要与 WebExtensions history搞混了,我们要说的这个history变化时是不会请求服务器的)。它暴露了很多有用的方法和属性,允许你在用户浏览历史中向前和向后跳转,同时——从HTML5开始——提供了对history栈中内容的操作。

history对象提供的API 可以实现无刷新更改地址栏链接,配合 AJAX 可以做到无刷新跳转,所以通过history进行路由变化是不会向服务器进行请求的

向前向后跳转

这两个api很简单,只是一条语句就好

history.back()//向后跳转
history.forward()//向前跳转

跳转到指定的点

此api调用方式为:

history.go(1)

go的参数为你要跳转到的url相对当前url的位置标志。向后跳转是负数,向前是正数。

这个api我个人觉得用处不是很大,因为我们操作url要么就是直接向后跳转,要么直接向前跳转,要么就是直接赋值url直接跳转过去。该api需要传入相对的位置标志就会显得略有些麻烦。

跳转到指定的页面

HTML5引入了 history.pushState() 和 history.replaceState() 方法,这两个方法都可以跳转到指定的url页面,主要区别在于replaceState() 是修改了当前的历史记录项而不是新建一个,也就是说history.pushState()之后,history.length会加一,但是replaceState()却不会。 注意这并不会阻止其在全局浏览器历史记录中创建一个新的历史记录项。

我们先来说history.pushState(),其调用方式如下:

history.pushState(state, title, url)

pushState() 需要三个参数: 一个状态对象, 一个标题 (目前被忽略), 和 (可选的) 一个URL. 让我们来解释下这三个参数详细内容:

  • 状态对象: 状态对象state是一个JavaScript对象,通过pushState () 创建新的历史记录条目。无论什么时候用户导航到新的状态,popstate事件就会被触发,能触发popstate事件的是history.back()或history.forword()以及history.go(),pushState()是不会触发的,后面会介绍如何监听pushState事件。该事件的state属性包含该历史记录条目状态对象的副本。
  • 标题:Firefox目前忽略这个参数,但未来可能会用到。传递一个空字符串在这里是安全的,而在将来这是不安全的。二选一的话,你可以为跳转的state传递一个短标题。
  • URL:该参数定义了新的历史URL记录。注意,调用 pushState() 后浏览器并不会立即加载这个URL,但可能会在稍后某些情况下加载这个URL,比如在用户重新打开浏览器时。新URL不必须为绝对路径。如果新URL是相对路径,那么它将被作为相对于当前URL处理。新URL必须与当前URL同源,否则 pushState() 会抛出一个异常。该参数是可选的,缺省为当前URL。

使用示例

history.pushState({msg:"跳转url"}, null, '/oecom');

调用完成以后,如果之前的url为https://www.oecom.cn/history , 那么调用完成以后就会变为:https://www.oecom.cn/oecom

然后我们再来说一下history.replaceState(),他的参数和pushState的参数相同,区别上面我们也说过了,下面我们来看一下具体是什么样子。

history.pushState({msg:"跳转url"}, null, '/oecom1');
history.pushState({msg:"跳转url"}, null, '/oecom2');
history.replaceState({msg:"跳转url"}, null, '/oecom3');

我们调用三次url跳转,前两次使用pushState,后一次使用replaceState,当我们在调用history.back()时,会直接跳转到oecom1路由上,原因就在于replaceState是直接将当前路由替换掉,而不是增加一个。

popstate事件

每当活动的历史记录项发生变化时, popstate 事件都会被传递给window对象。如果当前活动的历史记录项是被 pushState 创建的,或者是由 replaceState 改变的,那么 popstate 事件的状态属性 state 会包含一个当前历史记录状态对象的拷贝

window.onpopstate = function(event) {
  alert("location: " + document.location + ", state: " + JSON.stringify(event.state));
};
//绑定事件处理函数. 
history.pushState({page: 1}, "title 1", "?page=1");    //添加并激活一个历史记录条目 https://www.oecom.cn/example.html?page=1,条目索引为1
history.pushState({page: 2}, "title 2", "?page=2");    //添加并激活一个历史记录条目 https://www.oecom.cn/example.html?page=2,条目索引为2
history.replaceState({page: 3}, "title 3", "?page=3"); //修改当前激活的历史记录条目 https://www.oecom.cn/example.html?page=2 变为 https://www.oecom.cn/example.html?page=3,条目索引为3
history.back(); // 弹出 "location: https://www.oecom.cn/example.html?page=1, state: {"page":1}"
history.back(); // 弹出 "location: https://www.oecom.cn/example.html, state: null
history.go(2);  // 弹出 "location: https://www.oecom.cn/example.html?page=3, state: {"page":3}

history当前状态

页面加载时,或许会有个非null的状态对象。这是有可能发生的,举个例子,假如页面(通过pushState() 或 replaceState() 方法)设置了状态对象而后用户重启了浏览器。那么当页面重新加载时,页面会接收一个onload事件,但没有 popstate 事件。然而,假如你读取了history.state属性,你将会得到如同popstate 被触发时能得到的状态对象。

你可以读取当前历史记录项的状态对象state,而不必等待popstate 事件, 只需要这样使用history.state 属性:

let currentState = history.state;

上面我们说了popstate事件,这个事件无法监听pushState和replaceState事件,有一个很笨的方式就是采用setInterval轮询的方式来判断history.state是否变化来判断url是否变化,当然这个方法是很消耗性能的。

我们可以采用改写一下pushState方法来实现,思路是在history添加一个onpushState属性,在pushState时进行调用即可。

(function(history){
    var pushState = history.pushState;
    history.pushState = function(state) {
        if (typeof history.onpushstate == "function") {
            history.onpushstate({state: state});
        }
        return pushState.apply(history, arguments);
    };
})(window.history);
//设置其方法和popstate相同即可
window.onpopstate =history.onpushstate= function(event) {
      alert("location: " + document.location + ", state: " + JSON.stringify(event.state));
};

如果要监听replaceState,方法和此方法相同,在此不再赘述。

浏览器支持

Chrome

Safari

Firefox

Opera

IE

Android

iOS

31+

7.1+

34+

11.50+

10+

4.3+

7.1+

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 通过js对电话和姓名身份证等进行部分隐藏处理

    在进行web前端页面开发中,有时需要从后台获取用户数据来显示在前台页面,但是考虑到用户信息安全的问题,就需要对这些信息进行处理,使其不完全显示出来,例如姓名,两...

    无邪Z
  • 通过js判断访问设备是android还是IOS

    判断访问设备是android还是ios,无非就是获取设备的userAgent,下面来看一下通过正则表达式来判断访问设备是安卓还是苹果

    无邪Z
  • promise的使用方法

    异步操作作为JavaScript中的一大特色,解决了JavaScript单线程运行的一个难题,但是很多时候有问题也在于这上面。最大的问题之一,就是异步操作过多的...

    无邪Z
  • BOM相关知识

    history对象 history对象保存着用户上网的历史记录,从窗口被打开那一刻起。属于window对象的属性。 go()方法可以在用户的历史记录中任意跳转...

    wangxl
  • Harris角点检测原理分析

    看到一篇从数学意义上讲解Harris角点检测很透彻的文章,转载自:http://blog.csdn.net/newthinker_wei/article/det...

    一棹烟波
  • javascript入门笔记8-window对象

    History 对象 history对象记录了用户曾经浏览过的页面(URL),并可以实现浏览器前进与后退相似导航的功能。 注意:从窗口被打开的那一刻开始记录,...

    方志朋
  • 从let it go 到make it happen

    在这种情况下,一种倡导的方式就是let it go ,有些情况下,可能这种情况适得其反,就是这个问题真go了,我们直接忘记了或者放弃了,这样不好。

    jeanron100
  • 布隆过滤器(bloom filter)的原理及在推荐去重中的应用

    在业务中,我需要给每个用户保存1w条浏览记录,之后每一次的返回值都要和历史记录做一个去重,即保证用户不会重复看到同一篇文章.

    呼延十
  • CVPR 18论文“无法重现”?中国作者再度澄清获网友一边倒支持

    而对于机器学习的研究来说,由于各种超参数对结果影响很大,并且论文里不太可能把所有实现的细节都说清楚,公布代码就成了保证复现的重要途径。

    新智元
  • 016.Zabbix聚合监控

    用Calcuated Items可以对Items进行汇总计算,如求磁盘总容量、网络流量,只依赖于Zabbix-Server,与Zabbix-Agent和prox...

    木二

扫码关注云+社区

领取腾讯云代金券