前端实现路由的前提:
改变url不刷新页面,前端负责监听url的改变,并匹配相应的页面进行渲染。 改变url不刷新页面有2种方式:
- has
- H5 history
接下来对这两种方式进行详细介绍:
1.hash模式的实现原理
早期的前端路由的实现就是基于 location.hash 来实现的。 location.hash 的值就是 URL 中 # 后面的内容。比如下面这个网站,它的 location.hash 的值为 '#search':
https://www.word.com#search
hash 路由模式的实现主要是基于下面几个特性:
- URL 中 hash 值只是客户端的一种状态,也就是说当向服务器端发出请求时,hash 部分不会被发送;
- hash 值的改变,都会在浏览器的访问历史中增加一个记录。因此我们能通过浏览器的回退、前进按钮,或者调用history.go(),history.forward(),history.back(),控制hash的切换
- 获取hash值:window.location.hash
- 修改hash值:window.location.hash="#/index"
- 监听hash值的改变:可以使用 hashchange 事件来监听 hash 值的变化
window.addEventListener('hashchange', function(){console.log('hash变了!') }, false);
window.onhashchange =function onhashchange() {console.log('11111')}
2.history模式的实现原理
HTML5 引入了 history.pushState() 和 history.replaceState() 方法,它们分别可以添加和修改历史记录条目。这些方法通常与window.onpopstate 配合使用。
这两个 API 可以在不进行刷新的情况下,操作浏览器的历史纪录。唯一不同的是,前者是新增一个历史记录,后者是直接替换当前的历史记录,
用法如下所示:
window.history.pushState({data:'test1'}, 'title1', '/test1');
window.history.replaceState({data:'test2'}, 'title2', '/test2');
假如当前url为:mozilla.org/
- 使浏览器地址栏显示为
http://mozilla.org/test1,但并不会导致浏览器加载http://mozilla.org/test1,甚至不会检查http://mozilla.org/test1是否存在。 - pushState会在历史记录里添加一条新的记录
- replaceState会修改当前的历史记录
| 参数 | 用途 |
|---|---|
| 状态对象 | 关联到当前的url,当当前的url被激活,会将该状态对象的副本作为onPopState()回调函数的参数,也可以通过history.state获取到该对象的副本。 |
| 标题 | 目前的浏览器都忽略了这个参数 |
| URL | url,定义了新的历史 URL 记录,新的 URL 可以是与当前 URL 同源的任意 URL,也可以不传该参数,不传就为当前的url |
怎么监听url的变化?
- 通过windon.popstate()
当点击浏览器上的前进和后退按钮时,或者调用history.go(),history.forward(),history.back()时,会触发onpopstate事件。通过onpopstate可以监听用户点击前进后退造成的url的改变。
window.addEventListener('popstate', function(e) {
//e.state,history.state都是当前url的状态对象的副本
console.log('history.state',e.state,history.state)
console.log('onpopstate~');
});
window.onpopstate = function(){
console.log('有人操作了浏览器的历史记录~')
}
当使用pushState()和replaceSate()时,并不会触发onpopstate事件,所以需要对这两个方法进行改写 。 2. 改写history.pushState(),改写history.replaceSate()
const bindHistoryEvent = function(type) {
const historyEvent = history[type];
return function() {
const newEvent = historyEvent.apply(this, arguments); //执行history函数
const e = new Event(type); //声明自定义事件
e.arguments = arguments;
window.dispatchEvent(e); //抛出事件
return newEvent; //返回方法,用于重写history的方法
};
};
history.pushState = bindHistoryEvent('pushState');
history.replaceState = bindHistoryEvent('replaceState');
window.addEventListener('replaceState', function(e) {
console.log('replaceState~');
});
// history.state: 当前激活路径如果是使用pushState或者replaceState添加的记录,该值为传入的第一个参数
window.addEventListener('pushState', function(e) {
console.log('history.state',e.arguments,history.state)
console.log('pushState~');
});
3.两种模式如何选择?
| hash | history | |
|---|---|---|
| 优势 | 1.兼容性好 2.hash值不会发送到服务器不需要 | 1.url中没有# 2.设置的url与当前url一致也会添加一条新的记录 3.可以添加状态对象和title |
| 缺点 | 1.url中有# | 1.兼容性不好 2.path的部分会被发送到服务器,需要服务端配置 |
参考: