专栏首页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+

本文参与 腾讯云自媒体分享计划 ,欢迎热爱写作的你一起参与!
本文分享自作者个人站点/博客:https://www.oecom.cn复制
如有侵权,请联系 cloudcommunity@tencent.com 删除。
登录 后参与评论
0 条评论

相关文章

  • SPA(单页面应用)的基本实现原理

    我们应该都使用过网易云音乐或者是别的一些逼格比较高的网页,他们比较厉害的一点是页面看起来只有一个,不管你点击什么地方,永远不会刷新页面,都是感觉是在一个页面上完...

    何处锦绣不灰堆
  • React.js实战之Router原理及 React-router页面路由Hash 路由H5路由

    JavaEdge
  • VueRouter实现路由嵌套单页面展示

    明知山
  • 彻底理清前端单页面应用(SPA)的实现原理

    Peter谭金杰
  • SAP UI5应用里的页面路由处理

    选择SAP UI5应用的webapp文件夹,右键,选择New->SAP UI5 View, 新建一个UI5视图:

    Jerry Wang
  • SAP UI5应用里的页面路由处理

    选择SAP UI5应用的webapp文件夹,右键,选择New->SAP UI5 View, 新建一个UI5视图:

    Jerry Wang
  • Vue Router实现路由嵌套单页面展示

    明知山
  • Vue + Flask 实现单页面应用

    说明我们的前端代码构建成功。 现在我们在浏览器中打开上面的地址,就可以得到页面如下:

    周萝卜
  • 「源码解析 」这一次彻底弄懂react-router路由原理

    个人理解,单页面应用是使用一个html下,一次性加载js, css等资源,所有页面都在一个容器页面下,页面切换实质是组件的切换。

    用户6835371
  • 深入揭秘前端路由本质,手写 mini-router

    前端路由一直是一个很经典的话题,不管是日常的使用还是面试中都会经常遇到。本文通过实现一个简单版的 react-router 来一起揭开路由的神秘面纱。

    胡哥有话说
  • 前端路由的原理及应用

    早期的路由都是后端来实现的,根据用户访问的地址的不同,浏览器从服务器请求对应的资源或页面展示给用户。当页面数据量大,结构复杂的时候,随之造成服务器的压力也比较大...

    江米小枣
  • 前端路由三种模式原理

    整个页面重新加载,浏览器历史可以显示每一个地址。考虑到安全性但是JS代码中是无法操作的。 2. Hash路由方式。

    19组清风
  • 令人惊叹的前端路由原理解析和实现方式

    在单页应用如此流行的今天,曾经令人惊叹的前端路由已经成为各大框架的基础标配,每个框架都提供了强大的路由功能,导致路由实现变的复杂。想要搞懂路由内部实现还是有些困...

    腾讯技术工程官方号
  • 从vue-router源码中看前端路由的两种实现

    随着前端应用的业务功能越来越复杂、用户对于使用体验的要求越来越高,单页应用(SPA)成为前端应用的主流形式。大型单页应用最显著特点之一就是采用前端路由系统,通过...

    前端迷
  • 前端路由原理解析和实现

    路由的概念来源于服务端,在服务端中路由描述的是 URL 与处理函数之间的映射关系。

    ConardLi
  • 一文搞懂前端路由的原理(Vue、React、Angular)

    前端三大框架 Angular、React、Vue ,它们的路由解决方案 angular/router、react-router、vue-router 都是基于前...

    前端老道
  • [Vue 牛刀小试]:第十二章 - 使用 Vue Router 实现 Vue 中的前端路由控制

      前端路由是什么?如果你之前从事的是后端的工作,或者虽然有接触前端,但是并没有使用到单页面应用的话,这个概念对你来说还是会很陌生的。那么,为什么会在单页面应用...

    程序员宇说

扫码关注云+社区

领取腾讯云代金券