在前后端分离的项目中,前端一般使用 SPA 单页面应用模式来开发项目。那么,什么是 SPA 呢?
单页Web应用(single page web application,SPA),就是只有一张Web页面的应用,是加载单个HTML 页面并在用户与应用程序交互时动态更新该页面的Web应用程序。
我的理解:单页面应用就是改变页面的url地址,不会向后台发送请求,而是通过前端路由,动态渲染页面组件,url地址的改变与后台无关。
而前端路由又分为两种模式:
两种模式的对比:
对比 | hash 模式 | history 模式 |
---|---|---|
url 显示 | url 中带"#" | url 中不带"#" |
回车刷新(浏览器刷新按钮) | 页面正常显示 | 后端未配置则页面显示404 |
支持版本 | 支持低版本浏览器和 IE 浏览器 | HTML5 新推出的 API |
前端路由的本质,是监听 url 地址或 hash 值的改变,来切换并渲染对应的页面组件。
接下来让我们详细了解一下两种路由模式。
hash 模式是一种把前端路由的路径用 # 拼接在真实 url 后面的模式,通过 hashchange 事件监听 hash 值的改变来渲染页面对应的组件。hash 模式不会向后端发送 http 请求,所有的 hash 值操作都与后端无关。
使用 location.hash
获取 hash 值。
以 url 地址: http://localhost:8080/#/about
为例:
location 是 window.location
或者 document.location
的简写模式。
让我们来看看 location 有哪些常用属性:
以 url 地址:http://localhost:8080/#/about?name=she&age=16
示例:
属性 | 解释 | 值 |
---|---|---|
hash | 设置或返回从#开始的锚点信息 | #/about?name=she&age=16 |
host | 设置或返回主机名+端口号 | localhost:8080 |
hostname | 设置或返回主机名 | localhost |
port | 设置或返回端口号 | 8080 |
href | 设置或返回完整的 URL | http://localhost:8080/#/about?name=she&age=16 |
origin | 设置或返回协议+主机+端口 | http://localhost:8080 |
pathname | 设置或返回当前 URL 的路径 | /(hash 值不算在路径里面,此处返回的是#之前的斜杠) |
protocol | 设置或返回当前协议 | http: |
search | 设置或返回从?开始的查询字符串 | ?name=she&age=16 |
location 常用方法:
方法名 | 解释 | 语法 |
---|---|---|
assign() | 加载并显示指定的 URL 的内容 | location.assign(url); |
reload() | 刷新当前页面,就像刷新按钮一样 | location.reload(); |
replace() | 以给定的 URL 来替换当前的资源 | location.replace(url); |
调用 location.replace() 方法后,当前页面不会保存到会话历史中(session History),这样,用户点击回退按钮时,将不会再跳转到该页面。
当 URL 的片段标识符(hash 值)更改时,将触发 hashchange 事件 (跟在#符号后面的 URL 部分,包括#符号)。
使用 addEventListener 监听 hashchange 事件:
window.addEventListener('hashchange', function() {
console.log('hash值被修改了')
}, false);
使用 onhashchange 事件处理程序
function locationHashChanged() {
if (location.hash === '#/about') {
console.log("欢迎进入about页面");
}
}
window.onhashchange = locationHashChanged;
测试 hashchage 事件:
window.addEventListener('hashchange', function () {
console.log('hashchage 事件被触发了');
});
// hash值的改变也会触发 window.onpopstate事件,onpopstate事件在 history模式中再做介绍
window.addEventListener("popstate", () => {
console.log("popstate 事件被触发了");
})
直接修改浏览器url地址,添加 hash 值 #/about
。可以看出,修改 hash 值会优先触发 popstate 事件,然后再触发 hashchange 事件:
重新获取 hash 值:
history 是 HTML5 提供的新特性,允许开发者直接更改前端路由,也就是更改 url 地址而无需向后端发送 http 请求。
history 是 window.history
的简写模式,是 History 构造函数的实例化对象。
History 接口允许操作浏览器的曾经在标签页或者框架里访问的会话历史记录。
也就是说 History 里面保存着当前标签页的所有浏览页面的访问记录。
实例化对象属性
属性 | 解释 | 值 |
---|---|---|
length | 会话历史记录中元素的数目,包括当前加载的页面 | Integer(整型数值) |
scrollRestoration | 设置浏览器的默认滚动行为 | auto(默认值,浏览器自动滚动) / manual(关闭浏览器自动滚动) |
state | 返回一个表示历史堆栈顶部的状态的任意值 | any(任意值) |
实例化对象方法
方法名 | 解释 | 语法 |
---|---|---|
back() | 在会话历史记录中向后移动一页。如果没有上一页,则此方法调用不执行任何操作 | window.history.back() |
forward() | 在会话历史中向前移动一页 | window.history.forward(); |
go() | go方法从会话历史记录中加载特定页面 | window.history.go(-1); 负值表示向后移动back(),正值表示向前移动forward(); 值为0或不传时重新加载当前页面 |
pushState() | 向当前浏览器历史中添加记录 | history.pushState(state, title[, url]) |
replaceState() | 修改当前历史记录实体,可以更新 state 对象以及 URL 地址。 | history.replaceState(stateObj, title[, url]); |
history.pushState()的使用
history.pushState() 方法接收三个参数:
history.replaceState()的使用
history.replaceState() 方法接收的参数与 history.pushState() 方法相同,唯一的不同是,使用 replaceState 会更新当前页面的记录,包括 state 对象和 URL 地址。
举个例子,使用 pushState 添加 a.html b.html c.html
三个记录,然后使用 replaceState 添加 d.html
,最后查看历史记录栈中收录了几条历史记录:
由上图可知,历史记录栈中只存有三条记录,即 ["a.html", "b.html", "d.html"]
。原因就是 replaceState 将 c.html
替换为了 d.html
。
所以使用 history.back() 会返回 b.html
。
使用 history.forward() 也只会显示 d.html
:
window.onpopstate 事件是用来监听浏览历史记录变化的。
调用 history.pushState()
或者 history.replaceState()
不会触发 popstate 事件。popstate 事件只会在浏览器某些行为下触发,比如点击前进、后退按钮(或者在 JavaScript 中调用 history.back()
、history.forward()
、 history.go()
方法)。即,在同一文档的两个历史记录条目之间导航会触发该事件。
使用 addEventListener 监听 popstate 事件:
window.addEventListener('popstate', function(event) {
console.log(event);
}, false);
使用 onpopstate 事件处理程序
function historyStateChanged(event) {
console.log(event);
}
window.onpopstate= historyStateChanged;
测试,使用 popstate 监听记录栈的改变:
window.addEventListener("popstate", (event) => {
console.log(event);
})
使用 pushState 以及 replaceState 并未触发 popstate 事件:
使用 history.back() ,触发了 popstate 事件并打印了参数 event,从下图可以看出 event事件对象中保存着 state 对象。
在 history 下,你可以自由的修改 path,但刷新页面时,如果服务器中没有相应的响应或者资源,则会出现404页面,因为刷新页面会发送 http 请求。也就是说,使用 history 路由模式,需要通过服务端来允许地址可访问,后端也必须配置了当前资源路径地址才行。
如果后台部署使用了 nginx,可以对 nginx 进行如下配置来解决页面刷新问题(摘录):
server {
listen 8080;
server_name localhost;
location / {
root 'E:\dist';
index /index.html;
try_files $uri $uri/ /index.html;
}
}