前端实现路由的两种方式浏览器页面跳转是通过后台实现的,自从前后端分离,前端也把路由跳转给接管了,就是说 url 的改变不发送给服务器,而是让浏览器自己处理,这样有一个好处可以减轻服务器的压力,一般适用于 SPA。

目前实现前端路由有两种方式,一种是 hash,一种是用 history api

hash 路由

hash 路由实现的原理是通过监听 window 的hashchange事件,来做响应更新页面处理

hash 即 URL 中"#"字符后面的部分,有以下特点

  • hash 值不会随着请求发送给服务器
  • hash 改变不会导致重新加载页面

监听 hash 值改变

window.addEventListener(
'hashchange',
() => {
// ...路由跳转
},
false
);

hash 的读写

可以通过location.hash来读写 hash 值

hash 路由优缺点

优点:兼容低版本浏览器

缺点:不美观,会和锚点冲突

history api

hash 路由虽然可以满足前端路由跳转,但是不太美观,history api 可以解决这个问题,让 url 看起来跟原来服务器直接跳转方式没什么区别

  • length:history 栈中的记录数
  • back():返回上一页
  • forward():前进下一页
  • go(number|url):参数为负数返回,正数前进,**history.go(1) 相当于 history.forward() **

html5 提供两个新的方法pushState()replaceState()以及一个全局监听事件popstate

pushState

history.pushState(state, title, url)在 history 栈添加新记录

使用 pushState()可以改变 referrer,在用户发送 XMLHttpRequest 请求时会在 HTTP 头部的 referrer 会响应的改变,因为 referrer 是标识创建 XMLHttpRequest 对象时 this 所代表的 window 对象中 document 的 URL

假设在http://localhost:8080/index.html中执行了以下代码:

var state = { id: 123 };
history.pushState(state, null, 'a.html');

地址会变成http://localhost:8080/a.html,但是页面不会加载 a.html

replaceState

replaceState(state, title, url)是在 history 栈替换当前的记录,参数和效果与 pushState 类似

popstate 事件

每当处于激活状态的 history 栈发生变化时,popstate 事件就会在对应 window 对象上触发,但是注意的是 pushState 和 replaceState 不会触发 popstate 事件

popstate 事件只会在浏览器某些行为下触发, 比如浏览器后退、前进按钮,js 代码里的 history.back(),history.go(),history.forward(),location.hash

history.pushState({ id: 1 }, null, '?page=1');
history.pushState({ id: 2 }, null, '?page=2');
history.replaceState({ id: 3 }, null, '?page=3');
history.back();

此时的 url 是 http://localhost:8080/a.html,触发了 popstate 事件,event 对象的 state 值为{id: 1}

history.state

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

兼容低版本浏览器history api库,提供了跟 history 一样接口写法

hash 和 history 的区别

模式 优点 缺点
hash 使用简单、无需后台支持 1. 不美观,与锚点冲突
2. 在 url 中以 hash 形式存在,不会传到后台
history 地址美观,便于理解 需要后台或者服务器配合刷新跳转