Hash History
平时单页面网站经常使用的模式,# 有两种情况,一个是我们所谓的锚点,比如典型的回到顶部按钮原理、Github 上各个标题之间的跳转等,路由里的 # 不叫锚点,我们称之为 hash,大型框架的路由系统大多都是哈希实现的。
Angular1,vue-router,react-router都支持该模式
实现原理
改变#不触发网页重载
比如
http://www.example.com/index.html#location1
// 改成
http://www.example.com/index.html#location
浏览器不会重新向服务器请求index.html。
改变#会改变浏览器的访问历史
每一次改变#后的部分,都会在浏览器的访问历史中增加一个记录,使用"后退"按钮,就可以回到上一个位置。
onhashchange事件去监测hash的变更
三种实现方式
window.onhashchange = func;
<body onhashchange="func();">
window.addEventListener("hashchange", func, false);
具体实现代码
<button onclick="link('/a')">to /a</button>
<button onclick="link('/b')">to /b</button>
<script>
function link(url) {
console.log('link to: ' + url);
window.location = '#' + url;
}
window.addEventListener("hashchange", function() {
var hash = window.location.hash;
console.log('current hash: ' + hash);
}, false);
</script>
缺点
SEO
页面都变成了全JS生成,搜索引擎及第三方统计无法进行抓起
解决方案:
一:Google抓取AJAX方案
二:再做一个服务端生成内容的镜像网站
三:HTML5 history 中的PushState(另外一种路由模式)
HTML5 history
这种路由模式下,没有难看的#号,url与后台路由看起来没有差别。
vue-router,react-router都支持该模式
实现原理
HTML5 新增的历史记录 API 可以实现无刷新更改地址栏链接,配合 AJAX 可以做到无刷新跳转。
window.history.pushState(null, null, "/profile/");
popstate 事件
当用户点击浏览器的「前进」、「后退」按钮时,就会触发popstate事件。你可以监听这一事件,从而作出反应。
window.addEventListener("popstate", function(e) {
var state = e.state;
// do something...
});
这里e.state就是当初pushState时传入的第一个参数,state 对象可以是任何可以序列化的东西。由于 火狐 会将这些对象存储在用户的磁盘上,所以用户在重启浏览器之后这些state对象会恢复。参见 MDN pushState API
replaceState 方法
有时,你希望不添加一个新记录,而是替换当前的记录(比如对网站的 landing page),则可以使用replaceState方法。这个方法和pushState的参数完全一样。
具体实现代码
<button onclick="link('/a')">to /a</button>
<button onclick="link('/b')">to /b</button>
<script>
function link(url) {
console.log('link to: ' + url);
window.history.pushState(null, null, url);
}
// 仅仅调用pushState方法或replaceState方法 ,并不会触发该事件,只有用户点击浏览器倒退按钮和前进按钮,或者使用JavaScript调用back、forward、go方法时才会触发
window.addEventListener('popstate', function(e) {
alert("location: " + document.location + ", state: " + JSON.stringify(event.state));
}, false);
</script>
缺点
当使用HTML5 history,刷新页面时会出现404。是因为当前url服务端无法找到,所以,要在服务端增加一个覆盖所有情况的候选资源:如果 URL 匹配不到任何静态资源,则应该返回同一个 index.html 页面,这个页面就是你 app 依赖的页面。同时需要在前端这边处理404
参考:
[1] 阮一峰 URL的井号 http://www.ruanyifeng.com/blog/2011/03/url_hash.html
[2] orangexc 前端路由的两种实现原理 https://segmentfault.com/a/1190000007238999
[3] Renfei Song's Blog HTML5 简介(三):利用 History API 无刷新更改地址栏 https://www.renfei.org/blog/html5-introduction-3-history-api.html