SPA路由的实现原理

SPA 路由的核心在于浏览器 URL 的管理与对应视图的匹配。通常分为hash模式和history模式。

hash模式

通过触发 hashchange 事件,进而触发页面的更新逻辑。

什么是hash属性

URL 接口的 hash 属性是一个包含以 '#' 开头的 URL 片段标识符的字符串。 片段不会经过 URL 解码。如果某个 URL 没有片段,该属性会包含一个空字符串------""。

URL编码:百分号编码(英语:Percent-encoding),用于为application/x-www-form-urlencoded MIME准备数据,因为它用于通过HTTP的请求操作(request)提交HTML表单数据。

特点:

  1. 不发送至服务器:hash部分不会被浏览器发送到服务器。它仅在客户端使用。

  2. hash值改变不会被触发页面刷新

  3. 可以通过hashchange事件监听hash部分变化

实现

依赖window.hashchange方法

触发的情况:

  1. 修改 window.location.hash: 通过 JavaScript 直接更改 window.location.hash 的值。例如: javascript window.location.hash = '#newSection';
  2. 使用锚链接: 在 HTML 中,使用锚链接直接导航到新的 hash。例如:
html 复制代码
 <a href="#section2">Go to Section 2</a>

点击该链接后,浏览器会自动更新 URL 的 hash 部分。

  1. 浏览器导航按钮: 用户点击浏览器的前进或后退按钮,若 URL 的 Hash 发生改变,也会触发该事件。

  2. 通过 a 标签的 href 属性: 用户点击后,URL 发生变化,从而触发 hashchange 事件。

  3. 通过 JavaScript 的 location.assign() 方法: 利用 location.assign() 或 location.replace() 来导航到新的 Hash。例如:

javascript 复制代码
 location.assign('#yetAnotherSection');
 location.replace('#yetAnotherSection');

注意: history.pushState() 从未引起 hashchange 事件的触发,即使新 URL 与旧 URL 仅在 hash 上不同。

history模式

利用浏览器的历史记录栈改变当前页面的 URL, 同时,利用点击事件 结合 window.popState 监听事件触发页面的更新渲染逻辑。

History API

History 对象提供了操作浏览器会话历史(浏览器地址栏中访问的页面,以及当前页面中通过框架加载的页面)的接口。

History 接口允许操作浏览器的曾经在标签页或者框架里访问的会话历史记录。

chrome 浏览器中,可以将鼠标按住后退按钮,查看到 History 数组

  1. history.back = history.go(-1) = 浏览器后退键
  2. history.forward = history.go(1) = 浏览器前进键
  3. history.go
  • 通过当前页面的相对位置从浏览器历史记录(会话记录)异步加载页面。

  • 参数为 -1 的时候为上一页,参数为 1 的时候为下一页。

  • history.go(2)向前移动两页,history.go(-2)则向后移动两页。也可以理解为指针分别向栈底和栈顶移动2个位置。浏览器在往历史记录栈里面压入新的记录时,是直接在当前指针后面压入的,如果当前指针的后面,还有其它的记录项,都会被丢弃。

  • 当你指定了一个越界值(例如:当会话历史记录中没有之前访问的页面时,则传参的值为 -1,那么这个方法没有任何效果也不会报错。

  • 调用没有参数的 history.go() 方法或者参数值为 0 时,重新载入当前页面。 = location.reload()

  1. history.length 属性的值来确定历史记录栈中的页面数量
  2. history.pushState 类似于window.location = "#foo",它们都会在当前的文档中保留旧条目并创建和激活一个新的历史条目。 但是不会伴随着刷新
  3. history.replaceState replaceState将当前的会话页面的url替换成指定的数据,replaceState后也会改变当前页面的url,但是也不会刷新页面。

history.replaceState() 的操作与 history.pushState() 完全相同,只是 replaceState() 会修改当前历史记录条目而不是创建新的条目。就是都会改变当前页面显示的url,但都不会刷新页面。

popstate事件

history.back()、history.forward()、history.go()事件是会触发popstate事件的,但是history.pushState()、history.replaceState()不会触发popstate事件。

在单页应用中使用的是replaceState和pushState,如何监听replaceState和pushState行为

ts 复制代码
      (function (history) {
        // 自执行函数 (Immediately Invoked Function Expression, IIFE)
        // 用于避免污染全局作用域并立即执行包裹的代码
        // 传入 window.history 对象以便在函数内部进行操作
        var originalPushState = history.pushState;
        var originalReplaceState = history.replaceState;

        // 重写 pushState 方法
        history.pushState = function (state) {
          // 如果 history.onpushstate 是一个函数,则调用它
          if (typeof history.onpushstate == "function") {
            history.onpushstate({ state: state });
          }
          // 调用原始的 pushState 方法以保留其原有功能
          return originalPushState.apply(history, arguments);
        };

        // 重写 replaceState 方法
        history.replaceState = function (state) {
          // 如果 history.onreplacestate 是一个函数,则调用它
          if (typeof history.onreplacestate == "function") {
            history.onreplacestate({ state: state });
          }
          // 调用原始的 replaceState 方法以保留其原有功能
          return originalReplaceState.apply(history, arguments);
        };
      })(window.history);

      // 设置一个通用的事件处理函数用于拦截所有状态变化事件
      window.onpopstate =
        history.onpushstate =
        history.onreplacestate =
          function (event) {
            console.log("State changed:", event.state);
          };

两种不同模式的对比

特性 Hash 模式 History 模式
URL 美观性 较差,包含 # 美观,与传统 URL 一致
SEO 友好性 较差 较好
实现难度 简单,无需服务器配置 较复杂,不当的链接跳转可能会导致全页面重载。
刷新处理 无需特别处理 需要服务器端配置重定向
相关推荐
桂月二二3 小时前
探索前端开发中的 Web Vitals —— 提升用户体验的关键技术
前端·ux
CodeClimb4 小时前
【华为OD-E卷 - 第k个排列 100分(python、java、c++、js、c)】
java·javascript·c++·python·华为od
hunter2062064 小时前
ubuntu向一个pc主机通过web发送数据,pc端通过工具直接查看收到的数据
linux·前端·ubuntu
qzhqbb4 小时前
web服务器 网站部署的架构
服务器·前端·架构
刻刻帝的海角4 小时前
CSS 颜色
前端·css
浪浪山小白兔5 小时前
HTML5 新表单属性详解
前端·html·html5
lee5766 小时前
npm run dev 时直接打开Chrome浏览器
前端·chrome·npm
2401_897579656 小时前
AI赋能Flutter开发:ScriptEcho助你高效构建跨端应用
前端·人工智能·flutter
光头程序员6 小时前
grid 布局react组件可以循数据自定义渲染某个数据 ,或插入某些数据在某个索引下
javascript·react.js·ecmascript
limit for me6 小时前
react上增加错误边界 当存在错误时 不会显示白屏
前端·react.js·前端框架