浅谈前端路由原理hash和history

1、认识前端路由

本质

前端路由的本质,是监听 url 地址或 hash 值的改变,来切换渲染对应的页面组件

前端路由分为两种模式

hash 模式

history 模式

两种模式的对比

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 的重新加载、跳转等操作,方便地进行页面控制和导航

方法

  1. assign(url):加载(显示指定的 URL 的内容(加载新的文档并替换当前文档,类似于用户点击链接跳转页面。例如:window.location.assign('https://www.baidu.com'))
  2. replace(url):刷新当前页面(用新的文档替换当前文档,不会在历史记录中生成新的条目。例如:window.location.replace('https://www.baidu.com'))
  3. reload():替换当前的资源(重新加载当前文档。如果带有参数 forceReload 为 true,则会强制从服务器重新加载文档,而不使用缓存。例如:window.location.reload(true))。

(5)window.onhashchange 事件

当 URL 的片段标识符(hash 值)更改时,将触发 hashchange 事件 (跟在#符号后面的 URL 部分,包括#符号)。

可以使用 addEventListener 监听 hashchange 事件:

window.addEventListener('hashchange', function() {
  console.log('hash值被修改了');
  console.log('hashchage 事件被触发了');
}, false);

使用 onhashchange 事件处理程序

function locationHashChanged() {
  if (location.hash === '#/about') {
    console.log("欢迎进入about页面");
  }
}
window.onhashchange = locationHashChanged;
// 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

实例化对象属性

复制代码
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() 方法接收三个参数:

复制代码
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 事件:

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 对象。

4、解决history模式下页面刷新404问题

在 history 下,你可以自由的修改 path,但刷新页面时,如果服务器中没有相应的响应或者资源,则会出现404页面,因为刷新页面会发送 http 请求。也就是说,使用 history 路由模式,需要通过服务端来允许地址可访问,后端也必须配置了当前资源路径地址才行。

如果后台部署使用了 nginx,可以对 nginx 进行如下配置来解决页面刷新问题(摘录):

server {
  listen 8080;
  server_name  localhost;
  location / {
    alias /Data/nginx/portal/;
    index  /index.html;
    try_files $uri $uri/ /index.html;
  }
}

技术前沿拓展

前端开发,你的认知不能仅局限于技术内,需要发散思维了解技术圈的前沿知识。细心的人会发现,开发内部工具的过程中,大量的页面、场景、组件等在不断重复,这种重复造轮子的工作,浪费工程师的大量时间。

介绍一款程序员都应该知道的软件JNPF快速开发平台,很多人都尝试用过它,它是功能的集大成者,任何信息化系统都可以基于它开发出来。

这是一个基于 Java Boot/.Net Core 构建的简单、跨平台快速开发框架。前后端封装了上千个常用类,方便扩展;集成了代码生成器,支持前后端业务代码生成,实现快速开发,提升工作效率;框架集成了表单、报表、图表、大屏等各种常用的 Demo 方便直接使用;后端框架支持 Vue2、Vue3。如果你有闲暇时间,可以做个知识拓展。

看完本文如果觉得有用,记得点个赞支持,收藏起来说不定哪天就用上啦~

相关推荐
小华同学ai2 分钟前
ShowDoc:Star12.3k,福利项目,个人小团队的在线文档“简单、易用、轻量化”还专门针对API文档、技术文档做了优化
前端·程序员·github
Sudo_Wang17 分钟前
力扣150题
算法·leetcode·职场和发展
王解19 分钟前
Vue CLI 脚手架创建项目流程详解 (2)
前端·javascript·vue.js
刘大浪22 分钟前
vue.js滑动到顶便锁定位置
前端·javascript·vue.js
qystca26 分钟前
洛谷 P1595 信封问题 C语言dp
算法
小金刚®29 分钟前
构建简洁之美:我的第一个前端页面
前端
芳菲菲其弥章44 分钟前
数据结构经典算法总复习(下卷)
数据结构·算法
我是一只来自东方的鸭.1 小时前
1. K11504 天平[Not so Mobile,UVa839]
数据结构·b树·算法
ordinary901 小时前
指令-v-for的key
前端·javascript·vue.js
rpa_top1 小时前
RPA 助力电商:自动化商品信息上传,节省人力资源 —— 以影刀 RPA 为例【rpa.top】
大数据·前端·人工智能·自动化·rpa