乐趣区

history和hash详解

一、history

window.history(可直接写成 history)指向 History 对象,它表示当前窗口的浏览历史。
History 对象保存了当前窗口访问过的所有页面网址

1. length

   history.length 属性保存着历史记录的 url 数量,初始时该值为 1,如果当前窗口先后访问了三个网址,那么 history 对象就包括 3 项,history.length=3

2. 跳转方法:允许在浏览器历史之间移动

go()  接受一个整数为参数,移动到该整数指定的页面,比如 history.go(1)相当于 history.forward(),history.go(-1)相当于 history.back(),history.go(0)相当于刷新当前页面
back()  移动到上一个访问页面,等同于浏览器的后退键, 常见的返回上一页就可以用 back(),是从浏览器缓存中加载,而不是重新要求服务器发送新的网页
forward()  移动到下一个访问页面,等同于浏览器的前进键

如果移动的位置超出了访问历史的边界,以上三个方法并不报错,而是默默的失败

3.history.pushState()

在浏览器历史中添加记录

if(!!(window.hostory && history.pushState)) {// 支持 History API} else {// 不支持}

以上代码可以用来检查当前浏览器是否支持 History API。如果不支持的话可以考虑使用 Polyfill 库 History.js

history.pushstate()方法接受三个参数,以此为:

state: 一个与指定网址相关的状态对象,popState 事件触发时,该对象会传入回调函数,如果不需要这个对象,此处可填 null
title: 新页面的标题,但是所有浏览器目前都忽略这个值,因此这里可以填 null
url: 新的网址,必须与当前页面处在同一个域,浏览器的地址栏将显示这个网址

假定当前网址是 example.com/1.html,我们使用 pushState 方法在浏览记录 (history 对象) 中添加一个记录

var stateObj = {foo:'bar'};
history.pushState(stateObj,'page 2','2.html')

添加上边这个新纪录后,浏览器地址栏立刻显示 example.com/2.html,但不会跳转到 2.html,甚至也不会检查 2.html 是否存在,它只是成为浏览历史中的新纪录。这时,你在地址栏输入一个新的地址,然后点击了后退按钮,页面的 url 将显示 2.html;你再点击以此后退按钮,url 将显示 1.html
总之,pushState 方法不会触发页面刷新,只是导致了 history 对象发生变化,地址栏会有反应。
如果 pushState 的 url 参数,设置了一个新的锚点值(即 hash),并不会触发 hashChange 事件,如果设置了一个跨域网址,则会报错。

// 报错
history.pushState(null,null,'https://twitter.com/hello')

上边代码中,pushState()想要插入一个跨域的网址,导致报错,这样设计的目的是防止恶意代码让用户以为他们是在另一个网站上

4. history.replaceState()
history.replaceState()方法的参数和 pushState()方法一摸一样,区别是它修改浏览器历史当中的记录
假定当前页面是 example.com/example.html

history.pushState({page:1},'title 1','?page=1')
history.pushState({page:2},'title 2','?page=2')
history.replaceState({page:3},'title 3','?page=3')

history.back() //url 显示为 example.com/example.html?page=1
history.back() //url 显示为 example.com/example.html
history.go(2) //url 显示为 example.com/example.html?page=3

5. history.state 属性
history.state 返回当前页面的 state 对象

history.pushState({page:1},'title 1','?page=1')
history.state  //{page:1}

5. popState 事件
每当同一个文档的浏览历史(即 history)出现变化时,就会触发 popState 事件
需要注意:仅仅调用 pushState 方法或 replaceState 方法,并不会触发该事件,只有用户点击浏览器后退和前进按钮时,或者使用 js 调用 back、forward、go 方法时才会触发。另外该事件只针对同一个文档,如果浏览历史的切换,导致加载不同的文档,该事件不会被触发
使用的时候,可以为 popState 事件指定回调函数


window.onpopstate = function (event) {console.log('location:' + document.location);
  console.log('state:' +JSON.stringify(event.state));
};

// 或者

window.addEventListener('popstate', function(event) {console.log('location:' + document.location);
  console.log('state:' + JSON.stringify(event.state));
});

回调函数的参数是一个 event 事件对象,它的 state 属性指向 pushState 和 replaceState 方法为当前 url 所提供的状态对象(即这两个方法的第一个参数)。上边代码中的 event.state 就是通过 pushState 和 replaceState 方法为当前 url 绑定的 state 对象
这个 state 也可以直接通过 history 对象读取
history.state
注意:页面第一次加载的时候,浏览器不会触发 popState 事件

6.history.scrollRestoration

history.scrollRestoration = ‘manual’; 关闭浏览器自动滚动行为
history.scrollRestoration = ‘auto’; 打开浏览器自动滚动行为(默认)

二、hash
hash 就是指 url 尾巴后的 # 号以及后面的字符。这里的 # 和 css 里的 # 是一个意思。hash 也 称作 锚点,本身是用来做页面定位的,她可以使对应 id 的元素显示在可视区域内。

通过 window.location.hash 获取 hash 值

延伸:window.location 对象里面
hash:设置或返回从 (#) 开始的 URL(锚)。host:设置或返回主机名和当前 URL 的端口号。hostname:设置或返回当前 URL 的主机名。href:设置或返回完整的 URL。pathname:设置或返回当前 URL 的路径部分。port:设置或返回当前 URL 的端口号。search:设置或返回从问号 (?) 开始的 URL(查询部分)。assign():加载新的文档。reload():重新加载当前文档。replace():用新的文档替换当前文档。

hash 主要是一个事件:hashchange, 当 hash 值改变时会触发这个事件
语法:window.onhashchange = fun
或者:<body onhashchange=”fun”>
以上事件将覆盖现有的事件处理程序,为了添加一个新的事件处理程序,而不覆盖已有的其他的事件处理程序,可以使用函数 ”addEventListener”

window.addEventListener('hashchange',fun,false)

例子:

if('onhashchange' in window) {alert("该浏览器支持 hashchange 事件!");
}
function locationHashChanged() {if (location.hash === "#somecoolfeature") {somecoolfeature();
    }
}
window.onhashchange = locationHashChanged;

hashchange 事件有两个属性:

newURL: string 类型,当前页面新的 url
oldURL: string 类型,当前页面旧的 url


window.addEventListener('hashchange', function (e) {console.log(e.newURL,e.oldURL)
  var str = e.newURL.split('#')[1]
  document.getElementById('num').innerHTML = str.split('=')[1]
})

三、history 和 hash 两种模式的区别

我们都知道单页应用的路由有两种模式:hash 和 history。如果我们在 hash 模式时不使用 history.pushState() 和 history.replaceState() 方法,我们就只需要在 hashchange 事件回调里编写 url 改变时的逻辑就行了。而 history 模式下,我们不仅要在 popstate 事件回调里处理 url 的变化,还需要分别在 history.pushState() 和 history.replaceState() 方法里处理 url 的变化。而且 history 模式还需要后端的配合,不然用户刷新页面就只有 404 可以看了。

区别:

通过 history api,我们丢掉了丑陋的 #,但是它也有个问题:不怕前进,不怕后退,就怕刷新,f5,(如果后端没有准备的话), 因为刷新是实实在在地去请求服务器的, 真正的会导致浏览器向服务器发送请求,可以自由的修改 path,当刷新时,如果服务器中没有相应的响应或者资源,会分分钟刷出一个 404 来。我们需要在服务器端做处理:如果匹配不到任何静态资源,则应该始终返回同一个

在 hash 模式下,前端路由修改的是 #中的信息,比如这个 URL:http://www.test.com/#/hello,hash 的值为 #/hello。它的特点在于:hash 虽然出现在 URL 中,但不会被包括在 HTTP 请求中,对后端完全没有影响,因此改变 hash 不会重新加载页面。

为了解决 history 模式下刷新出现的 404 问题, 后台需要做一些路由支持, 比如当 url 不匹配时跳转到 index.html.

如果 Vue 后台部署使用的是 Nginx, 可以这样配置

location / {
   try_files $uri $uri/ /index.html;# 支持 history 模式刷新
   root   /www/test/hello/;
   index   index.html;
}
退出移动版