react react-router-dom history 实现原理,看这篇就够了

一 浏览器原生history对象

浏览器原生的 history 对象提供了对浏览器会话历史的访问,允许你在不同的页面之间进行导航。以下是 history 对象的主要特性和方法:

主要特性

  1. 会话历史

    • 浏览器的会话历史记录包括用户在当前窗口或标签页中访问过的所有页面。
    • 每个窗口或标签页都有自己的 history 对象。
  2. 状态管理

    • history 对象可以存储每个历史记录条目的状态信息,这些状态信息可以是任何 JavaScript 对象。

主要方法

  1. back()

    • 返回上一个历史记录条目,相当于点击浏览器的后退按钮。
    javascript 复制代码
    window.history.back();
  2. forward()

    • 前进到下一个历史记录条目,相当于点击浏览器的前进按钮。
    javascript 复制代码
    window.history.forward();
  3. go(delta)

    • 导航到指定的历史记录条目。delta 是一个整数,表示相对于当前条目的偏移量。正数表示前进,负数表示后退。
    javascript 复制代码
    window.history.go(-1); // 等同于 back()
    window.history.go(1);  // 等同于 forward()
  4. pushState(state, title, url)

    • 向历史记录栈中添加一个新的条目,但不会触发页面重新加载。
    • state:一个 JavaScript 对象,用于存储与新历史记录条目关联的状态信息。
    • title:一个字符串,用于描述新历史记录条目。现代浏览器通常忽略此参数。
    • url:一个字符串,表示新历史记录条目的 URL。
    javascript 复制代码
    window.history.pushState({ page: 1 }, "", "/page1");
  5. replaceState(state, title, url)

    • 替换当前的历史记录条目,但不会触发页面重新加载。
    • 参数与 pushState 相同。
    javascript 复制代码
    window.history.replaceState({ page: 2 }, "", "/page2");

事件

  1. popstate 事件

    • 当历史记录条目发生变化时(例如用户点击后退或前进按钮),会触发 popstate 事件。
    • 事件对象的 state 属性包含与当前历史记录条目关联的状态信息。
    javascript 复制代码
    window.addEventListener('popstate', (event) => {
      console.log('New state:', event.state);
    });

示例代码

以下是一个简单的示例,展示了如何使用 pushStatepopstate 事件:

html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>History Example</title>
</head>
<body>
  <button id="back">Back</button>
  <button id="forward">Forward</button>
  <button id="push">Push State</button>
  <button id="replace">Replace State</button>

  <script>
    document.getElementById('back').addEventListener('click', () => {
      window.history.back();
    });

    document.getElementById('forward').addEventListener('click', () => {
      window.history.forward();
    });

    document.getElementById('push').addEventListener('click', () => {
      window.history.pushState({ page: 1 }, "", "/page1");
    });

    document.getElementById('replace').addEventListener('click', () => {
      window.history.replaceState({ page: 2 }, "", "/page2");
    });

    window.addEventListener('popstate', (event) => {
      console.log('New state:', event.state);
    });
  </script>
</body>
</html>

总结

浏览器原生的 history 对象提供了强大的导航功能,允许你在不重新加载页面的情况下管理会话历史。通过 pushStatereplaceState 方法,你可以动态地更新 URL 和历史记录,而 popstate 事件则允许你监听历史记录的变化。这些功能在单页应用(SPA)中特别有用。

二 window 可以监听那些 state事件

在浏览器中,window 对象可以监听多种与状态相关的事件。这些事件通常用于响应用户的交互或页面状态的变化。以下是常见的几种状态事件及其用途:

  1. popstate 事件 描述:当活动的历史记录条目更改时触发,通常是因为用户点击了浏览器的后退或前进按钮,或者通过 history.back()、history.forward()、history.go() 方法导航。 用途:常用于单页应用(SPA)中,当用户导航时更新应用的状态。 示例:
javascript 复制代码
// javascript:
window.addEventListener('popstate', (event) => {
  console.log('New state:', event.state);
});
  1. hashchange 事件 描述:当当前窗口或框架的 URL 的片段标识符(即哈希值,URL 中 # 后面的部分)改变时触发。 用途:常用于基于锚点的导航,例如在单页应用中切换不同的视图。 示例:
javascript 复制代码
// javascript
window.addEventListener('hashchange', (event) => {
  console.log('Old URL:', event.oldURL);
  console.log('New URL:', event.newURL);
});
  1. beforeunload 事件 描述:在窗口、文档和资源即将被卸载时触发。这是一个询问用户是否确认离开页面的机会。 用途:常用于提示用户保存未保存的数据或确认是否离开页面。 示例:
ini 复制代码
// javascript
window.addEventListener('beforeunload', (event) => {
  // 设置对话框的文本
  const message = '确定要离开吗?';
  event.returnValue = message;
  return message;
});
  1. unload 事件 描述:在窗口、文档和资源已经被卸载时触发。注意,由于安全原因,现代浏览器限制了在这个事件中可以做的事情。 用途:常用于清理资源或发送最后的请求。 示例:
javascript 复制代码
// javascript
window.addEventListener('unload', (event) => {
  console.log('Page is being unloaded.');
});
  1. pageshow 事件 描述:当一个页面被显示时触发。这包括页面首次加载和从缓存中恢复的情况。 用途:可以用来检测页面是首次加载还是从缓存中恢复。 示例:
javascript 复制代码
// javascript
window.addEventListener('pageshow', (event) => {
  if (event.persisted) {
    console.log('Page is restored from cache.');
  } else {
    console.log('Page is loaded for the first time.');
  }
});
  1. pagehide 事件 描述:当一个页面被隐藏时触发。这包括页面卸载和浏览器标签页切换的情况。 用途:可以用来检测页面是否被隐藏或卸载。 示例:
javascript 复制代码
// javascript
window.addEventListener('pagehide', (event) => {
  if (event.persisted) {
    console.log('Page is being hidden but not unloaded.');
  } else {
    console.log('Page is being unloaded.');
  }
});

总结 这些事件可以帮助你更好地控制和响应浏览器中的各种状态变化,特别是在开发单页应用(SPA)时非常有用。通过监听这些事件,你可以确保用户在导航或离开页面时得到适当的反馈和处理。

三 react-router-dom useHistory history 实现原理

react-router-dom 中的 useHistory 钩子提供了对浏览器历史记录对象的访问,允许你在 React 组件中导航。下面是 useHistoryhistory 对象实现原理的简要说明:

  • history 对象react-router-dom 内部使用了一个名为 history 的库来管理浏览器的历史记录。这个库创建了一个 history 对象,该对象封装了浏览器的 window.history API,并提供了额外的方法来操作历史记录。

  • useHistory 钩子useHistory 是一个 React 钩子,它从 react-router-dom 的路由上下文中获取 history 对象。这个钩子返回一个 history 对象,你可以使用它来执行导航操作,如 pushreplacegoBack 等。

实现步骤

  1. 创建 history 对象

    • react-router-dom 初始化时,会创建一个 history 对象。这个对象可以是浏览器的历史记录对象,也可以是内存中的历史记录对象(用于服务器端渲染或测试)。
  2. 提供 history 对象

    • react-router-dom 使用 Router 组件将 history 对象传递给所有嵌套的路由组件。这通常是通过 BrowserRouterHashRouter 组件完成的。
  3. 使用 useHistory 钩子

    • 在 React 组件中,你可以使用 useHistory 钩子来获取 history 对象。这个钩子会从当前的路由上下文中读取 history 对象,并将其返回给组件。

示例代码

jsx 复制代码
import { useHistory } from 'react-router-dom';

function MyComponent() {
  const history = useHistory();

  function handleClick() {
    history.push('/new-page');
  }

  return (
    <button onClick={handleClick}>
      Go to New Page
    </button>
  );
}

关键方法

  • push(path, [state]):向历史记录栈中添加一个新的条目,并导航到该路径。
  • replace(path, [state]):替换当前的历史记录条目,而不是添加新的条目。
  • goBack():返回上一个历史记录条目。
  • goForward():前进到下一个历史记录条目。
  • listen(listener):注册一个监听器,当历史记录发生变化时调用该监听器。

总结

useHistory 钩子通过 react-router-dom 提供的路由上下文,让你可以在 React 组件中方便地访问和操作浏览器的历史记录。这个钩子依赖于 history 库来管理历史记录,提供了丰富的导航功能。

五 送一个小小的原生js实现SPA小案例

history 怎么实现 SPA 应用,react vue 框架 其实也是这个原理

在单页应用(Single Page Application, SPA)中,history 对象是实现无刷新导航的关键。通过使用 history API,你可以在不重新加载整个页面的情况下动态地更新 URL 和页面内容。以下是实现 SPA 应用中使用 history 的基本步骤和示例代码。

基本步骤

  1. 初始化历史记录管理

    • 创建一个 history 对象来管理应用的历史记录。
  2. 设置初始路由

    • 根据当前的 URL 初始化应用的状态。
  3. 监听 popstate 事件

    • 监听 popstate 事件,以便在用户点击浏览器的后退或前进按钮时更新应用的状态。
  4. 定义路由和路由处理函数

    • 定义应用的路由和相应的处理函数,用于根据 URL 更新页面内容。
  5. 导航函数

    • 提供导航函数,用于在应用内部导航到不同的路由。

示例代码

以下是一个简单的示例,展示了如何使用 history API 实现一个基本的 SPA 应用。

HTML 结构

html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>SPA Example</title>
</head>
<body>
  <nav>
    <a href="#/home" onclick="navigate('/home')">Home</a>
    <a href="#/about" onclick="navigate('/about')">About</a>
    <a href="#/contact" onclick="navigate('/contact')">Contact</a>
  </nav>
  <div id="app"></div>

  <script src="app.js"></script>
</body>
</html>

JavaScript 逻辑

javascript 复制代码
// app.js

// 初始化历史记录
const history = window.history;

// 获取当前路径
const currentPath = window.location.pathname;

// 路由配置
const routes = {
  '/home': () => renderContent('<h1>Home Page</h1>'),
  '/about': () => renderContent('<h1>About Page</h1>'),
  '/contact': () => renderContent('<h1>Contact Page</h1>')
};

// 渲染内容的函数
function renderContent(content) {
  document.getElementById('app').innerHTML = content;
}

// 导航函数
function navigate(path) {
  history.pushState({}, '', path);
  handleRoute(path);
}

// 处理路由
function handleRoute(path) {
  const routeHandler = routes[path] || routes['/404'];
  routeHandler();
}

// 初始化应用
function initApp() {
  handleRoute(currentPath);

  // 监听 popstate 事件
  window.addEventListener('popstate', (event) => {
    handleRoute(window.location.pathname);
  });
}

// 启动应用
initApp();

详细解释

  1. HTML 结构

    • 包含一个简单的导航栏和一个用于显示内容的 div 元素。
    • 导航链接使用 onclick 事件调用 navigate 函数,而不是默认的链接行为。
  2. JavaScript 逻辑

    • 初始化历史记录 :使用 window.history 对象来管理历史记录。
    • 获取当前路径 :使用 window.location.pathname 获取当前的 URL 路径。
    • 路由配置 :定义一个对象 routes,其中键是路径,值是对应的处理函数。
    • 渲染内容的函数renderContent 函数用于将内容渲染到 div 元素中。
    • 导航函数navigate 函数使用 history.pushState 方法更新 URL 并调用 handleRoute 函数来处理新的路径。
    • 处理路由handleRoute 函数根据路径调用相应的处理函数。
    • 初始化应用initApp 函数初始化应用,处理初始路径并监听 popstate 事件。

总结

通过使用 history API,你可以实现一个基本的 SPA 应用,实现在不重新加载页面的情况下动态更新 URL 和页面内容。这种方法在现代前端框架(如 React、Vue 和 Angular)中也非常常见,它们通常内置了更高级的路由管理功能。

PS:学会了记得,点赞,评论,收藏,分享

相关推荐
GISer_Jing2 小时前
前端面试通关:Cesium+Three+React优化+TypeScript实战+ECharts性能方案
前端·react.js·面试
落霞的思绪2 小时前
CSS复习
前端·css
咖啡の猫4 小时前
Shell脚本-for循环应用案例
前端·chrome
百万蹄蹄向前冲7 小时前
Trae分析Phaser.js游戏《洋葱头捡星星》
前端·游戏开发·trae
朝阳5817 小时前
在浏览器端使用 xml2js 遇到的报错及解决方法
前端
GIS之路7 小时前
GeoTools 读取影像元数据
前端
ssshooter8 小时前
VSCode 自带的 TS 版本可能跟项目TS 版本不一样
前端·面试·typescript
你的人类朋友8 小时前
【Node.js】什么是Node.js
javascript·后端·node.js
Jerry9 小时前
Jetpack Compose 中的状态
前端
dae bal9 小时前
关于RSA和AES加密
前端·vue.js