导读
在很久以前,每次切换 url 浏览器整个页面都会刷新一次。有时候只是想切换一点内容,url 也变变化了,那整个浏览器页面也跟着变了,体验非常不好。但是随着技术的进步,出现了 AJAX技术,AJAX 可以让网页的页面局部内容更新,url 可以保持不变。但是这个时候又发现一个问题,我页面利用 AJAX 更新的内容。我重新刷新浏览器,内容又重新变回了老样子。为了解决这个问题,出现了单页面应用。
什么叫单页面应用?其实就是只有一张Web页面的应用,是加载单个HTML 页面并在用户与应用程序交互时动态更新该页面的Web应用程序。只需观察路由(url 连接)变化,我就切换这个页面的内容,不需要页面重新刷新,用户体验更好。实现这个路由功能,就需要这2种路由的模式:hash 模式与 history 模式。
单页面路由实现功能
在实现这个路由,我们需要考虑3种情况。
1.输入 url 能切换页面
2.浏览器前进与后退按钮,可以对应切换页面
3.浏览器刷新,页面加载内容与原来一样
实现浏览器只需刷新一次,然后根据 url 的变化,切换页面功能。那我们需要利用 js 事件监听 url 变化。那如何监听 url 变化呢?那我们下面来讲2种路由的模式:hash 模式与 history 模式。
hash 模式
监听浏览器 url 上的 hash 值,切换浏览器对应的页面。
什么是 hash 值?hash 值是一个可读可写的字符串,该字符串是 URL 的锚部分(从 # 号开始的部分)。可以通过 window.location.hash 获取。举个例子: www.baidu.com/#home。 那这个 hash 值就是 #home 了。
hase 模式与 history 模式明显的区别就是 url 上 锚点(#)符号
那我们可以利用 浏览器事件 onhashchange
事件监听 url(window.location.hash
) 变化 ,切换不同页面,执行对应页面的js。需要注意几点:
- hash 值指的是地址中#号以及后面的字符,也称作锚点或散列值
- hash 值不会发送到服务器,改变 hash 值浏览器不会重新加载
- location.hash 值的变化会直接反应到浏览器地址栏
触发 hashchange 几种情况:
- url 上 hash 值的变化,都会触发 hashchange 变化
- 当 url 地址中有 hash 值,输入浏览器
www.baidu.com/#home
,先请求服务器www.baidu.com
,然后浏览器再触发onhashchange
事件 - html中
<a>
标签的属性 href 可以设置为页面的元素ID如 #top,当点击该链接时页面跳转至该id元素所在区域,同时浏览器自动设置window.location.hash
属性,地址栏中的 hash 值也会发生改变,并触发onhashchange
事件
总结: 只要浏览器 url 中带有 # 号,并且 hash 变化了,都会触发
onhashchange
事件。
模拟一下对应原理:
<style>
div {
display: none;
}
</style>
<!-- home、community、user、404表示4个不同页面 -->
<div class="home">首页</div>
<div class="community">社区</div>
<div class="user">我的</div>
<div class="error">404</div>
// 监听 hash 变化,点击浏览器的前进后退会触发
window.addEventListener('hashchange', function () {
// 获取 hash 值,不包括#号,例如:www.baidu.com/#home,获取到 home 字符串标识
var hashVal = window.location.hash.split('#')[1];
// 获取对应页面的标识
var showPage = document.querySelector('.' + hashVal);
// 根据 hash 值变化,展示不同页面内容。没有就 404
if (showPage) {
showPage.style.display = 'block';
} else {
document.querySelector('.error').style.display = 'block';
}
})
history 模式
History 对象包含用户(在浏览器窗口中)访问过的 URL。 History 对象是 window 对象的一部分,可通过 window.history 属性对其进行访问。它表示当前窗口的浏览历史。当页面发生改变时,只会改变页面的路径,不会刷新页面。 所以我们操作 History 对象 就相当于操作浏览器上对应的 url。
利用H5的 history中新增的两个API pushState() 和 replaceState() 和一个事件onpopstate监听URL变化
History 对象属性
属性 | 说明 |
---|---|
length | 返回历史列表中的网址数 |
History 对象方法
方法 | 说明 |
---|---|
back() | history.back(); 加载 history 列表中的前一个 URL |
forward() | history.forward(); 加载 history 列表中的下一个 URL |
go() | history.go(); 加载 history 列表中的某个具体页面 |
pushState() | 该方法用于在历史中添加一条记录。pushState()方法不会触发页面刷新,只是导致 History 对象发生变化,地址栏会有变化。语法:history.pushState(object, title, url) |
replaceState() | 该方法用来修改 History 对象的当前记录,用法与 pushState() 方法一样。 |
以上方法很少用,了解即可,具体用到再查一下文档。下面说到的就是核心了
popstate 事件
每当 history 对象出现变化时,就会触发 popstate 事件。
那什么时候触发?我罗列了一下:
触发时机 | 说明 |
---|---|
back() | history.back(); 加载 history 列表中的前一个 URL |
forward() | history.forward(); 加载 history 列表中的下一个 URL |
go() | history.go(); 加载 history 列表中的某个具体页面 |
那 js 监听这个浏览器触发 popstate 事件做对应的操作。popstate事件指定回调函数,回调函数的参数是一个 event 事件对象,它的 state 属性指向当前的 state 对象。
模拟一下对应原理:
// 监听 popstate 变化,点击浏览器的前进后退会触发
window.addEventListener('popstate', function () {
// e.state 相当于 history.state
console.log(history.state);
// 这里似
})
history模式的问题
通过history api,我们丢掉了丑陋的 #,但是它也有个问题:不怕前进,不怕后退,就怕刷新,F5,(如果后端没有准备的话),因为刷新是实实在在地去请求服务器的,不玩虚的。 在hash模式下,前端路由修改的是#中的信息,而浏览器请求时是不带它玩的,所以没有问题。 但是在history下,你可以自由的修改path,当刷新时,如果服务器中没有相应的响应或者资源,会分分钟刷出一个404来。
结语
目前我们用的 React 与 Vue 框架所用的路由都有这两种模式选择。
所以希望看完这篇文章对你有帮助,文中如有错误,欢迎在评论区指正,如果这篇文章帮助到了你,
🌹🌹🌹欢迎点赞和关注🌹🌹🌹,后续会输出更好的分享。
欢迎关注公众号:【程序员米粉】
公众号分享开发编程、职场晋升、大厂面试经验