HTML5实现无刷新修改URL

前言

今天在作一个vue的搜索功能,须要从搜索结果页面跳转到细节页面,而后点击返回还能返回到刚刚的结果页面,若是只用window.history.go(-1)固然会从新刷新搜索页面,固然是不行的。html

我尝试了俩种方式来修改url:vue

  1. window.location.href,拼接一个搜索的key值,点击搜索的同时,刷新了页面,url改变了,功能是实现了,但是bug来了...,搜索页面闪烁后才进入结果页,而结合不跳转页面就不会发生闪烁的状况,因此当页面刷新时,vue实例都会被从新加载。
  2. window.location.hash,url中#为网页中的一个位置,当读取此url时,页面会自动滚动到锚点处。这种方法不会向服务器发送请求,也不会重载页面。可读写。读取时,能够用来判断网页状态是否改变;写入时,则会在不重载网页的前提下,创造一条访问历史记录。

新增API

百度后发现HTML5为window.history对象引入了两个新的api是history.pushState()history.replaceState() 方法,它们分别能够添加和修改历史记录条目。这些方法一般与window.onpopstate事件配合使用。两种api都能改变当前的url,不一样的是,pushState在浏览器中建立一条新的历史记录,而history.replaceState仅仅替换当前地址为指定url。react

HTML5 新增的历史记录 API 都可以实现无刷新更改地址栏连接,配合 AJAX 彻底能够作到无刷新跳转。他们的做用很是大,能够作到改变网址却不须要刷新页面,这个特性后来用到了单页面应用中好比:vue-router,react-router-dom里面。那么下面介绍新API的使用方法vue-router

pushState方法

上面提到的这套 API 提供一种「人为操纵」浏览器历史记录的方法。api

咱们能够将浏览器历史记录能够看做一个「栈」。栈是一种后进先出的结构,能够把它想象成一摞盘子,用户每点开一个新网页,都会在上面加一个新盘子,叫「入栈」。用户每次点击「后退」按钮都会取走最上面的那个盘子,叫作「出栈」。而每次浏览器显示的天然是最顶端的盘子的内容。浏览器

语法:window.history.pushState(state, title, url);

执行pushState函数以后,会往浏览器的历史记录中建立一条新记录,同时改变地址栏的地址内容。它能够接收三个参数,按语法顺序分别为:服务器

  1. 一个state对象或者字符串,用于描述新记录的一些特性。这个参数会被一并添加到历史记录中,以供之后使用。这个参数是开发者根据本身的须要自由给出的。该值是后期能够经过history.state;获取当前页面的参数,
  2. 一个字符串,表明新页面的标题。当前基本上全部浏览器都会忽略这个参数。
  3. 一个字符串,表明新页面的相对地址。(必须与当前页面处在同一个域。)

简单例子:假设当前页面为renfei.org/,打开控制台执行下面的 JavaScript 语句:react-router

window.history.pushState({id: 1,name: "profile1"}, "My Profile", "/profile/");  //第一二参数可忽略设置为null

以后,地址栏的地址就会变成renfei.org/profile/,但同时浏览器不会刷新页面,甚至不会检测目标页面是否存在。dom

replaceState方法

有时,你但愿不添加一个新记录,而是替换当前的记录,俩者区别在于 replaceState() 是修改了当前的历史记录项而不是新建一个。函数

replaceState和pushState原理同样使用也同样,最经常使用的方法:

window.history.replaceState({id: 1,name: "profile"},'下载','download?id=1');

特色:replaceState不会加入到历史记录里面,用history.go(-1)会跳过当前页面至关因而history.go(-2)。

popstate事件

当页面加载时,它可能会有一个非空的state对象。这可能发生在当页面设置一个state对象(使用pushState或者replaceState)以后用户重启了浏览器。当页面从新加载,页面将收到onload事件,但不会有popstate事件。然而,若是你读取history.state属性,将在popstate事件发生后获得这个state对象

var currentState = history.state; //获得当前页面设置的history.pushState的state对象参数值

调用 history.pushState() 或者 history.replaceState() 不会触发 popstate 事件。 popstate事件只会在浏览器某些行为下触发, 好比点击后退、前进按钮(或者在JavaScript中调用history.back()、history.forward()、history.go()方法)。

例子:假设当前页面地址是http://example.com/index.html

window.onpopstate = function(event) {
  alert("location: " + document.location.href + ", state: " + JSON.stringify(event.state));//拿到history.state对象值
};
//绑定事件处理函数. 
history.pushState({page: 1}, "title 1", "?page=100");    //添加并激活一个历史记录条目 http://example.com/index.html?page=100,条目索引为1    //history.state为{page: 1}
history.pushState({page: 2}, "title 2", "?page=200");    //添加并激活一个历史记录条目 http://example.com/index.html?page=200,条目索引为2
history.replaceState({page: 3}, "title 3", "?page=300"); //修改当前所在页面激活的历史记录条目 http://ex..?page=200 变为 http://ex..?page=300,条目索引为3

history.back(); // 弹出 "location: http://example.com/index.html?page=100, state: {"page":1}"
history.back(); // 弹出 "location: http://example.com/index.html, state: null
history.go(2);  // 弹出 "location: http://example.com/index.html?page=300, state: {"page":3}

pushState方法与设置hash值的区别

在某种意义上,调用 pushState() 与 设置 window.location = "#foo" 相似,两者都会在当前页面建立并激活新的历史记录。但 pushState() 具备以下几条优势:

  1. 新的 URL 能够是与当前URL同源的任意URL 。而设置 window.location 仅当你只修改了哈希值时才保持同一个文件。
  2. 若是须要,能够没必要改变URL就能建立一条历史记录。而设置 window.location.hash = "#foo";只有在当前哈希不是 #foo 的状况下, 才会建立一个新的历史记录项。
  3. 咱们能够为新的历史记录项关联任意数据。而基于哈希值的方式,则必须将全部相关数据编码到一个短字符串里。

须要注意的是经过history.pushState修改网页的URL地址,在配合相关代码显示隐藏相应界面即可以实现单页面多界面相互操做。该方法比直接访问URL地址速度快,执行效率高,UI体验好,但会增长页面的复杂性及耦合性,要视实际状况而定,通常都用在dialog弹出框上。

应用:全站 AJAX,并使浏览器可以抓取 AJAX 页面

这个能够干啥用?一个比较经常使用的场景就是,配合 AJAX。

假设一个页面左侧是若干导航连接,右侧是内容,同时导航时只有右侧的内容须要更新,那么刷新整个页面无疑是浪费的。这时咱们可使用 AJAX 来拉取右面的数据。可是若是仅仅这样,地址栏是不会改变的,用户没法前进、后退,也没法收藏当前页面或者把当前页面分享给他人;搜索引擎抓取也有困难。这时,就可使用 HTML5 的 History API 来解决这个问题。

思路:首先绑定click事件。当用户点击一个连接时,经过preventDefault函数防止默认的行为(页面跳转),同时读取连接的地址(若是有 jQuery,能够写成$(this).attr('href')),把这个地址经过pushState塞入浏览器历史记录中,再利用 AJAX 技术拉取(若是有 jQuery,可使用$.get方法)这个地址中真正的内容,同时替换当前网页的内容。

为了处理用户前进、后退,咱们监听popstate事件。当用户点击前进或后退按钮时,浏览器地址自动被转换成相应的地址,同时popstate事件发生。在事件处理函数中,咱们根据当前的地址抓取相应的内容,而后利用 AJAX 拉取这个地址的真正内容,呈现,便可。

最后,整个过程是不会改变页面标题的,能够经过直接对document.title赋值来更改页面标题。