1. 路由
- 传统的路由指的是:当用户访问一个
url
时,对应的服务器会接收这个请求,然后解析url中的路径,从而执行对应的处理逻辑。这样就完成了一次路由分发。 - 而前端路由是不涉及服务器的,是前端利用
hash
或者HTML5
的history API
来实现的,一般用于不同内容的展示和切换。
目前Vue
推荐单页面应用 SPA
开发模式,大型单页应用最显著特点之一就是采用前端路由系统,通过改变URL
,在不重新请求页面的情况下,更新页面视图。Vue
中的路由解决方案为vue-router
。
它提供两种不同方式的路由方式:Hash
和 History
,在 vue-router
中是使用了外观模式将几种不同的路由方式提供了一个一致的高层接口,让我们可以更解耦的在不同路由方式中切换。Hash
和 History
除了外观上的不同之外,还有一个区别是:Hash
方式的状态保存需要另行传递,而 HTML5 History
原生提供了自定义状态传递的能力,我们可以直接利用其来传递信息。
在vue-router
中是通过mode
这一参数控制路由的实现模式的:
2. Hash
Hash
方法是在路由中带有一个 #
,主要原理是通过监听 #
后的 URL
路径标识符的更改而触发的浏览器 hashchange
事件,然后通过获取 location.hash
得到当前的路径标识符,再进行一些路由跳转的操作,参见 MDN:
location.href
:返回完整的URL
;location.hash
:返回URL
的锚部分;location.pathname
:返回URL
路径名;hashchange
事件:当location.hash
发生改变时,将触发这个事件。
比如访问一个路径 http://sherlocked93.club/base/#/page1
,那么上面几个值分别为:
# http://sherlocked93.club/base/#/page1
{
"href": "http://sherlocked93.club/base/#/page1",
"pathname": "/base/",
"hash": "#/page1"
}
看一个例子:
<html>
<head>
<style>
li {
cursor: pointer;
}
</style>
</head>
<body>
<div id="main">
<p>router-hash</p>
<ul id="list">
<li><a href="#router1">router1</a></li>
<li><a href="#router2">router2</a></li>
<li><a href="#router3">router3</a></li>
</ul>
<div id="panel"></div>
</div>
<script>
window.onload = function () {
window.onhashchange = function (opt) {
console.log('hash has changed to: ', location.hash)
}
}
</script>
</body>
</html>
效果:
当我们点击后退时,可见路由跳转回router2
中。并且当我们刷新页面时不会报出404
的错误,那是因为hash
虽然出现在URL
中,但不会被包括在HTTP
请求中。它是用来指导浏览器动作的,对服务器端完全无用。
3. History API
HTML5
的History API
为浏览器的全局history
对象增加的扩展方法。一般用来解决ajax
请求无法通过回退按钮回到请求前状态的问题。
在HTML4
原有的和HTML5
新提出的history
的API有:
history.forward()
; //在历史记录中前进一步history.back()
; //在历史记录中后退一步history.go(n)
: //在历史记录中跳转n步骤,n=0为刷新本页,n=-1为后退一页。history.pushState(data, title, url);
//向历史记录中追加一条记录history.replaceState(data, title, url);
//替换当前页在历史记录中的信息history.state;
//是一个属性,可以得到当前页的state
信息。window.onpopstate;
//是一个事件,在点击浏览器后退按钮或js
调用forward()
、back()
、go()
时触发。监听函数中可传入一个event
对象,event.state
即为通过pushState()
或replaceState()
方法传入的data
参数。
以一个例子实现一下:
<html>
<head>
<style>
li {
cursor: pointer;
}
</style>
</head>
<body>
<div id="main">
<p>vue-router</p>
<ul id="list">
<li>router1</li>
<li>router2</li>
<li>router3</li>
</ul>
<div id="panel"></div>
</div>
<script>
window.onload = function () {
window.onpopstate = function (event) {
console.log(event)
};
document.querySelector('#list').addEventListener('click', function (event) {
if(event.target.nodeName.toLowerCase()==="li") {
var content = event.target.innerHTML;
var _newState;
_newState = {
url: location.origin + '/' + content,
title: document.title,
state: content
};
window.history.pushState(_newState, '', '/' + content);
console.log('you have change the router to: ', content);
}
})
}
</script>
</body>
</html>
效果:
总而言之就是当浏览器访问一个页面时,当前地址的状态信息会被压入历史栈,当调用history.pushState()
方法向历史栈中压入一个新的state
后,历史栈顶部的指针是指向新的state
的。可以将其作用简单理解为假装已经修改了url
地址并进行了跳转 ,除非用户点击了浏览器的前进、回退,或是显式调用HTML4
中的操作历史栈的方法,否则不会触发全局的popstate
事件。以上例子中点击后退可见由当前的router2
跳转到router3
,控制台打印出popstate
事件信息。
当我们刷新页面的时候,会报出404
的错误:
这也是history
不同于hash
的一个地方,是因为history
模式则会将URL
修改得就和正常向后端发起请求一样,如果后端没有配置对应的路由处理,则会返回404
错误。
官方推荐的解决办法是在服务端增加一个覆盖所有情况的候选资源:如果 URL
匹配不到任何静态资源,则应该返回同一个 index.html
页面,这个页面就是你 app
依赖的页面。
参考:
https://www.cnblogs.com/dashnowords/p/9671213.html 大史不说话
https://juejin.im/post/5c52da9ee51d45221f242804 SHERlocked93