❤ 前端路由原理hash和history
1、认识前端路由
本质
前端路由的本质,是监听 url 地址或 hash 值的改变,来切换渲染对应的页面组件
前端路由分为两种模式
- hash 模式
- history 模式
两种模式的对比
对比 | hash 模式 | history 模式 |
---|---|---|
url 显示 | url 中带"#" | url 中不带"#" |
回车刷新(浏览器刷新按钮) | 页面正常显示 | 后端未配置则页面显示404 |
支持版本 | 支持低版本浏览器和 IE 浏览器 | HTML5 新推出的 API |
2、hash 模式
(1)hash 定义
hash 模式是一种把前端路由的路径用 # 拼接在真实 url 后面的模式
在hash模式下,本质上是修改window.location.href实现的。
前端路由的改变依托于#锚点,而锚点后边的值我们可以通过修改window.location.hash的值来修改,每一次hash值的变化都会导致触发hashchange这个事件,hash模式就是通过 hashchange 事件来 监听 hash 值的改变从而渲染页面对应的组件。
在hash模式下修改页面不会刷新,因此hash模式不会向后端发送http请求,不会导致浏览器向后端发送请求。
(2)location
使用 location.hash
获取 hash 值
location 是 window.location
或者 document.location
的简写模式,window.location
对象包含了当前 URL 的信息,并提供了一系列属性来访问和操作 URL。
(3)location 的常用属性
href
:获取或设置完整的 URL。host
:获取或设置主机名和端口号。hostname
:获取或设置主机名。protocol
:获取或设置协议部分(如 "http:" 或 "https:")。pathname
:获取或设置 URL 的路径部分。search
:获取或设置 URL 的查询部分(即问号后面的部分,包括问号)。hash
:获取或设置 URL 的片段标识部分(即井号后面的部分,包括井号)。origin
:获取只读属性,表示 URL 的源部分(协议+主机+端口)。
通过访问 window.location
对象的这些属性,可以方便地获取当前页面的 URL 信息,并且在需要时进行相应的修改。这些属性对于处理 URL 相关的逻辑非常有用。
(4)location 常用方法
作用: 实现对当前页面 URL 的重新加载、跳转等操作,方便地进行页面控制和导航
方法:
assign(url)
:加载
(显示指定的 URL 的内容(加载新的文档并替换当前文档,类似于用户点击链接跳转页面。例如:window.location.assign('https://www.example.com')
))。replace(url)
:刷新当前页面
(用新的文档替换当前文档,不会在历史记录中生成新的条目。例如:window.location.replace('https://www.example.com')
)。reload()
:替换当前的资源
(重新加载当前文档。如果带有参数forceReload
为 true,则会强制从服务器重新加载文档,而不使用缓存。例如:window.location.reload(true)
)。
(5)window.onhashchange 事件
当 URL 的片段标识符(hash 值)更改时,将触发 hashchange 事件 (跟在#符号后面的 URL 部分,包括#符号)。
可以使用 addEventListener 监听 hashchange 事件:
js
window.addEventListener('hashchange', function() {
console.log('hash值被修改了');
console.log('hashchage 事件被触发了');
}, false);
使用 onhashchange 事件处理程序
js
function locationHashChanged() {
if (location.hash === '#/about') {
console.log("欢迎进入about页面");
}
}
window.onhashchange = locationHashChanged;
js
// hash值的改变也会触发 window.onpopstate事件,onpopstate事件在 history模式中再做介绍
window.addEventListener("popstate", () => {
console.log("popstate 事件被触发了");
})
直接修改浏览器url地址,添加 hash 值 #/about。可以看出,修改 hash 值会优先触发 popstate 事件,然后再触发 hashchange 事件:
3、history 模式
(1)history 定义
history 是 HTML5 提供的新特性,允许开发者直接更改前端路由,也就是更改 url 地址而无需向后端发送 http 请求。
history 是 window.history 的简写模式,是 History 构造函数的实例化对象。
History 接口允许操作浏览器的曾经在标签页或者框架里访问的会话历史记录。
也就是说 History 里面保存着当前标签页的所有浏览页面的访问记录。
(2)history API
实例化对象属性
scss
length 会话历史记录中元素的数目,包括当前加载的页面
Integer(整型数值)
scrollRestoration 设置浏览器的默认滚动行为 auto(默认值,浏览器自动滚动) / manual(关闭浏览器自动滚动)
state 返回一个表示历史堆栈顶部的状态的任意值
any(任意值)
实例化对象方法
scss
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() 方法接收三个参数:
csharp
state:一个对象,popState 事件触发时,state 对象会传入回调函数。如无需传参,则设置为 null 。
title:新页面的标题,但是所有浏览器目前都忽略这个值,因此可以设置为空字符串 "" 或者 null 。
url:新的网址地址,必须与当前页面处于同一个域下,浏览器的地址栏将显示这个网址。
接下来做一个测试,有一个 a.html 页面:
使用 pushState 方法添加一条记录到 History 会话历史中,并传参, pushState 方法只是更新了 url 地址,而页面未跳转。
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 事件
window.onpopstate 事件是用来监听浏览历史记录变化的。
调用 history.pushState() 或者 history.replaceState() 不会触发 popstate 事件。popstate 事件只会在浏览器某些行为下触发,比如点击前进、后退按钮(或者在 JavaScript 中调用 history.back()、history.forward()、 history.go() 方法)。即,在同一文档的两个历史记录条目之间导航会触发该事件。
使用 addEventListener 监听 popstate 事件:
js
window.addEventListener('popstate', function(event) {
console.log(event);
}, false);
使用 onpopstate 事件处理程序
js
function historyStateChanged(event) {
console.log(event);
}
window.onpopstate= historyStateChanged;
js
测试,使用 popstate 监听记录栈的改变:
window.addEventListener("popstate", (event) => {
console.log(event);
})
使用 pushState 以及 replaceState 并未触发 popstate 事件:
js
使用 history.back() ,触发了 popstate 事件并打印了参数 event
event事件对象中保存着 state 对象。
4、解决history模式下页面刷新404问题
在 history 下,你可以自由的修改 path,但刷新页面时,如果服务器中没有相应的响应或者资源,则会出现404页面,因为刷新页面会发送 http 请求。也就是说,使用 history 路由模式,需要通过服务端来允许地址可访问,后端也必须配置了当前资源路径地址才行。
如果后台部署使用了 nginx,可以对 nginx 进行如下配置来解决页面刷新问题(摘录):
js
server {
listen 8080;
server_name localhost;
location / {
root 'E:\dist';
index /index.html;
try_files $uri $uri/ /index.html;
}
}