React Router 的实现原理

news/2024/12/28 21:42:37/文章来源:https://www.cnblogs.com/wpshan/p/18547808
 

本文分两部分,一说前端路由的基本原理,二说 React Router 的实现原理

前端路由的基本原理​

不说屁话,从时间线上讲,Web 应用原本是后端渲染,后来随着技术的发展,有了单页面应用,慢慢从后端渲染发展成前端渲染

在博客前端路由hash、history的实现 一问中我已经介绍过这两种模式

hash 模式

  • 通过监听 hashchange 事件,匹配 hash 值并渲染页面模块

history 模式

  • 利用 history.pushState + popState 实现
  • history.pushState 能实现不刷新页面,但往历史栈中新增一个记录
  • popState 则会在历史记录条目被更改时,触发
  • pushState 只会改变历史栈,修改它没有什么API可以监听,所以要与 popState配合

简单来说,前端路由的基本原理无非要实现两个功能:

  • 监听记录路由变化
  • 匹配路由变化并渲染内容

hash模式​

hash 模式的实现比较简单,我们通过 hashChange 事件就能直接监听到路由 hash 的变化,并根据匹配到的 hash 的不同来渲染不同的内容。

<body><a href="#/home">Home</a><a href="#/user">User</a><a href="#/about">About</a><div id="view"></div></body><script>function onHashChange() {const view = document.getElementById("view");switch (location.hash) {case "#/home":view.innerHTML = "Home";break;case "#/user":view.innerHTML = "User";break;case "#/about":view.innerHTML = "About";break;default:view.innerHTML = "Home";break;}}window.addEventListener("hashchange", onHashChange);</script>

history模式​

  1. 拦截 a 标签的点击事件,阻止它的默认跳转行为
  2. 使用 H5 的 history API 更新 URL
  3. 监听和匹配路由改变以更新页面

利用 pushState 往历史栈中添加记录且不刷新页面的特性 + 监听 popstate 浏览器操作导致的 URL 变化

  • window.history.pushState(state, title, path)

  • window.addEventListener("popstate", onPopState)

    <body><a href="/home">Home</a><a href="/user">User</a><a href="/about">About</a><div id="view"></div>
    </body>
    <script>const elements = document.querySelectorAll("a[href]");elements.forEach(el =>el.addEventListener("click", e => {e.preventDefault();const test = el.getAttribute("href");history.pushState(null, null, el.getAttribute("href"));onPopState();}));function onPopState() {const view = document.getElementById("view");switch (location.pathname) {case "/home":view.innerHTML = "Home";break;case "/user":view.innerHTML = "User";break;case "/about":view.innerHTML = "About";break;default:view.innerHTML = "Home";break;}}window.addEventListener("popstate", onPopState);
    </script>

hash模式 VS History模式​

模式优点缺点
Hash 浏览器兼容性更好,不需要后端路由支持 有 # 号
History 需要现代浏览器,需要后端路由支持 无 # 号

history 模式下当页面刷新时,因为找不到相对应的路由(因为只有一个页面,路由由前端控制),所以会报404错误,需要在 Nginx(或者服务器)中配置,如果找不到ur,则返回 首页

nginx
location / {
    try_files $uri $uri/ /index.html;
}

React Router 的实现原理​

先用最简单的话来概括一下 React Router 到底做了什么?

本质上, React Router 就是在页面 URL 发生变化的时候,通过我们写的 path 去匹配,然后渲染对应的组件。

那么,我们想一下如何分步骤实现:

  1. 如何监听 url 的变化 ?
  2. 如何匹配 path ?
  3. 渲染对应的组件

换句话说,也是一个组件,通过渲染不同的组件来控制路由切换

整体设计​

我们用一张图来理解一下整个 react-router 是怎么实现的:

接下来我们看看每一个步骤是怎么实现的。

640

如何监听 url 的变化 ?​

正常情况下,当 URL 发生变化时,浏览器会像服务端发送请求,但使用以下 3 种办法不会向服务端发送请求:

  • 基于 ajax(实现起来很麻烦)
  • 基于 hash
  • 基于 history

react-router 使用了 history 这个核心库。它本质是屏蔽不同模式下载监听实现上的差异,使用发布订阅模式 来实现,这里不做探究

总结​

从后端路由到前端路由,最大的改变是体验,体验更良好了

前端路由模式有两种:hash 模式 和 history 模式,两者分别利用浏览器自由特性实现单页面导航

  • hash 模式:window.location 或 a 标签改变锚点值,window.hashchange() 监听锚点变化
  • history 模式:history.pushState()、history.replaceState() 定义目标路由,window.popstate() 监听浏览器操作导致的 URL 变化

React Router 匹配路由由 mathPath 通过 path-to-regexp 进行,<Route> 相当于一个高阶组件,以不同的优先级和匹配模式渲染匹配到的子组件

React Router 的主要组件源码,整体的实现:

  • 对于监听功能的实现,React Router 引入了 history 库,以屏蔽了不同模式路由在监听实现上的差异, 并将路由信息以 context 的形式,传递给被 <Router> 包裹的组件, 使所有被包裹在其中的路由组件都能感知到路由的变化, 并接收到路由信息
  • 在匹配的部分, React Router 引入了 path-to-regexp 来拼接路径正则以实现不同模式的匹配,路由组件·<Route>作为一个高阶组件包裹业务组件, 通过比较当前路由信息和传入的 path,以不同的优先级来渲染对应组件

参考资料​

  • 深入浅出解析 React Router 源码

  • 单页面应用路由实现原理:以 React-Router 为例

  • 剖析单页面应用路由实现原理

  • SPA 路由三部曲之核心原理

  • SPA 路由三部曲之实战演练

  • 图解 React-router 带你深入理解路由本质

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/834013.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[TJOI2007] 路标设置

[[TJOI2007] 路标设置(https://www.luogu.com.cn/problem/P3853) 题目 B 市和 T 市之间有一条长长的高速公路,公路上相邻路标的最大距离定义为该公路的“空旷指数”。现在公路上增设一些路标,使得公路的“空旷指数”最小。请设计一个程序计算能达到的最小值是多少。请注意,公…

TBM810-ASEMI贴片桥堆8A 1000V

TBM810-ASEMI贴片桥堆8A 1000V编辑:ll TBM810-ASEMI贴片桥堆8A 1000V 型号:TBM810 品牌:ASEMI 封装:TBM-4 特性:贴片桥堆 正向电流:8A 反向耐压:1000V 恢复时间:>2000ns 引脚数量:4 芯片个数:4 芯片尺寸:50MIL 浪涌电流:50A 漏电流:>10uA 工作温度:-55℃~150…

Python开发环境搭建(PyCharm+Anaconda+Git+Gitee)

一、Anaconda介绍 1.1 为什么选择Anaconda? Anaconda是一个开源的Python发行版本,主要用于数据科学和机器学习,它包含了Python、conda以及众多工具和库,让我们可以轻松的构建和管理python虚拟环境,方便的进行python项目开发。下面是选择Anaconda的理由:安装简单方便 便捷…

Visual Studio 快速分析 .NET Dump 文件

前言 在开发和维护 .NET 应用程序的过程中,有时会遇到难以捉摸的性能瓶颈或内存泄漏等问题。这些问题往往发生在生产环境中,难以复现。为了更准确地诊断这些运行时问题,通常会收集应用程序在生产环境中的内存转储文件(.dump 文件)。在这种情况下,分析内存转储文件(.dump…

使用model-viewer加载glb文件

实现效果代码 react项目引入方式可选其一1.在html中引入 <script type="module" src="https://unpkg.com/@google/model-viewer/dist/model-viewer.min.js"></script>npm 或者 yarn安装yarn add @google/model-viewerindex.jsimport React, { …

关于定时器周期、频率等相关计算

1、定时器作为计数器,时钟频率计算如下图: 2、定时器产生一次更新中断时间计算,如下: 当定时器设置为边沿对齐模式和向上计数模式时,定时器计数到重装载值(arr)产生一次中断,产生一次中断的时间为: 其中:T为定时器周期,也为此处产生一次中断的时间;arr为重装载值…

useCallback 和 useMemo 使用场景

一切为了性能,无论是 useCallback 还是 useMemo 还是 memo,都是为了让不该渲染的组件不去渲染 在学习 useCallback、useMemo 之前,我们需要知道一点,React 的渲染是自顶而下,如果父组件渲染了,那么子组件也会渲染,其子孙组件“世世代代”都要渲染 但如果父组件的渲染与子…

[LeetCode 最大岛屿面积

### DFS解法``` python class Solution:dir = [(-1,0),(1,0),(0,-1),(0,1)]def dfs(self,grid,x,DFS解法 class Solution:dir = [(-1,0),(1,0),(0,-1),(0,1)]def dfs(self,grid,x,y):if x < 0 or x >= len(grid) or y < 0 or y >= len(grid[0]) or grid[x][y] != 1…

【Ubuntu】在Ubuntu上安装微信

【Ubuntu】在Ubuntu上安装微信 零、说明 微信官网最近发布了Linux的测试版本,实际使用下来与Windows版本相差不多,本文介绍如何在Ubuntu(Debian系)上安装Linux版本的微信。 壹、下载 打开Linux微信官网:https://linux.weixin.qq.com/,根据自己的处理器架构选择对应的deb格…

文件共享服务之NFS挂载实验

任务需求 1.部署一台web服务器,提供静态网页的展示,该网站的html等静态资源远程存储在NFS服务器。 2.部署NFS服务器,创建共享文件夹(提供静态文件),发布该共享目录,提供给web服务器使用。 主机列表 # 外网地址 内网地址 主机名 192.168.122.207…

第 5 篇 Scrum 冲刺博客

团队作业4——第 5 篇 Scrum 冲刺博客 作业要求这个作业属于哪个课程 https://edu.cnblogs.com/campus/gdgy/CSGrade22-34这个作业要求在哪里 https://edu.cnblogs.com/campus/gdgy/CSGrade22-34/homework/13234这个作业的目标 团队集体协作完成项目开发队名 雄狮般的男人站立式…

Sitecore debug 工具

由于 Sitecore 的调试需要老是通过 log 来分析,而每次更新 dll 都需要等待一定的时间和重复执行前端的操作逻辑,特开发一个在线编辑器的方式来方便调试。警告:请勿在生产环境使用。截图构建项目 打开 Frontend,使用 pnpm i 安装依赖包,然后 pnpm build:vite 构建项目,之后…