路由跳转时的页面状态保存

news/2024/4/29 7:37:23/文章来源:https://blog.csdn.net/web220507/article/details/126941230

前言

我们在开发网页时,经常会遇到一种情况——在一个页面对页面初始状态进行了修改(如已请求到的数据、表单数据、滚动高度等等),跳转到另外一个页面之后再返回到原页面(路由回退),原页面需要保持原先的状态不变。但如果不做额外处理,往往会出现原页面历史状态丢失、被重置为初始状态的情况。

SPA 与 MPA

1. SPA(单页应用)

这种情况常见会出现在 SPA 中,因为 SPA 是使用单页来模拟多页,页面跳转时并没有请求一个新的页面应用,所以 SPA 在路由跳转时会丢失历史页面的普通状态。

2. MPA(多页应用)

是否在 MPA 中就不会出现这种情况了呢?也不尽其然。

在多年以前,互联网上全都是些简单的 MPA 应用,一个页面就是一个 html 文件,一个网站就是一组 html。而一个页面的所有数据,就都在这个 html 文件(或者其他资源文件)中,在浏览器获取到 html 等资源文件的时候,也就随之获取到了数据。这种情况下,不会出现路由跳转导致丢失数据的情况,而滚动高度会被浏览器保存,所以只有一些表单等用户输入的信息会丢失。

但是,随着前端技术的发展和潮流的变化,更多复杂的情况出现了,比如无限下拉滚动加载数据,比如 h5 移动端常见的数据使用 ajax 加载,带来了更多的变化。

数据使用 ajax 加载,意味着数据与 html 的完全解耦。这种情况下,即使是 MPA ,也会出现路由跳转导致数据丢失的情况。

因为这种情况在移动端很常见,部分 Android 浏览器与大多数 IOS 浏览器针对这一现象做了额外的优化,在路由跳转后仍保留原页面资源,这样就避免了路由跳转导致原页面数据丢失。

各平台情况

1. PC 端

各种 PC 端浏览器历史悠久,基本都没有做额外优化,只会保存滚动高度。

但是对于这种情况,参考主流网站与用户习惯,页面往往不会直接跳转(window.location.href),而是打开新的标签页(window.open),这样问题就不存在了。

2. 移动端

上面也提到了,部分 Android 浏览器与大多数 IOS 浏览器针对这一现象做了额外的优化,在路由跳转后仍保留原页面资源,这样就避免了路由跳转导致原页面数据丢失。

3. 移动端 APP 内 WebView

参考主流 APP,在 APP 内使用 WebView 打开网页时,往往会在页面跳转时,新开一个 WebView。这样通过打开多个 WebView,来模拟原生页面路由跳转的体验。

总结

针对 MPA ,大部分情况下有简单的解决方法/平台自动优化,而 SPA 在各平台路由跳转都会丢失状态。所以我们主要分析处理 SPA 的问题。

SPA 解决思路

1.全局数据:将页面数据全都放在全局,这样不论页面如何跳转,数据都不会丢失。1.使用 redux/mobx 等状态管理方案2.数据保存于全局变量中
2.页面长留(keep-alive): 对于 SPA 应用,可以在页面跳转时,不卸载页面组件,而是将页面设置为不可见,等到回退到该页面时将页面设置为可见。1.react-keep-alive/react-keeper 等方案2.Vue 的 keep-alive 组件
3.存取数据:页面跳转时将数据储存起来,等到回退到该页面时将数据取出放入状态中。页面长留方案

react-keep-alive

  • 不基于 React Router,因此可以在任何需要缓存的地方使用它。* 因为并不是使用 display: none | block 来控制的,所以可以使用动画。* 通过 React.createPortal API 实现了这个效果。react-keep-alive 有两个主要的组件 <Provider><KeepAlive><Provider> 负责保存组件的缓存,并在处理之前通过 React.createPortal API 将缓存的组件渲染在应用程序的外面。缓存的组件必须放在 <KeepAlive> 中,<KeepAlive> 会把在应用程序外面渲染的组件挂载到真正需要显示的位置。react-keeper

实现基于重写路由库,提供一套新的 React Router

存取数据方案

由于业务上的需求,我们实现了一套基于存取数据的页面状态保存方案:

存取数据有以下问题需要思考:

1.如何建立页面和数据的映射关系?2.数据的垃圾回收机制?其实这两个问题有一定的交集,如果我们建立了页面和数据的映射关系,针对目前路由的状态,如果某个页面并不是可后退到的页面,就可以清除该页面的数据。

1. 页面的 key

我们可以发现,React Router 在路由跳转时,对每一个页面(除首页外)都有唯一的 window.history.state.key ,所以可以使用该值作为每个页面和该页面数据的 key,将数据存放在一个全局的 map 中。

2. 历史路由栈

对于浏览器的路由栈(history 对象),我们并没有办法直接获取到其中的详细路由信息。那么怎么办呢?我们可以用 js 维护一份本应用内的历史路由栈。

基于 react-router-dom 的 history:

  • 初始化时:将当前页面的 historyInfo 推入 historyStack* 使用 history.listen,当路由变化时,在 historyStack 中入栈出栈。* ⚠️注意的问题1.history.listen 无法区分浏览器的前进/后退按钮操作,都会被识别为 POP 后退解决方案:通过 historyStack 中有无当前页面的 key 来判断前进/后退history.listen((historyInfo: HistoryInfo, historyAction: HistoryAction) => {// ...if (historyAction === 'POP') {// 点击浏览器前进后退时,historyAction均为POP,所以要判断是否是前进let isGoForward = false;if (historyInfo.key) {const have = historyStack.find((h) => h.key === historyInfo.key);if (!have) {isGoForward = true;}}if (isGoForward) {historyStack.push(historyInfo);} else {historyStack.pop();}}// ...});``````2.点击浏览器刷新按钮进行刷新操作,会导致 app 重启,丢失路由栈数据解决方案:页面刷新时,history.state.key 不会变。所以在卸载时储存 historyStack 数据,初始化时取数据,并用 history.state.key 判断是否是刷新。(为什么不直接用 history.state 储存 historyStack 数据?因为unload事件中无法使用 history.replaceState 修改 history.state,只能在 beforeunload 事件中;而在 ios Safari 中,beforeunload 事件无法正常生效)/** * 处理浏览器刷新导致 historyStack 丢失的问题 */if (isBrowser) {// 取数据try {const storageHistoryStack = JSON.parse(sessionStorage.getItem(HISTORY_STACK) || ‘’,) as HistoryInfo[] | undefined;const length = storageHistoryStack?.length;if (length &&length > 1 &&storageHistoryStack[length - 1].key === getPageKey()) {for (const historyInfo of storageHistoryStack) {historyStack.push(historyInfo);}}} catch (e) {}// 页面离开时存数据window.addEventListener(‘unload’, () => {if (historyStack.length > 1) {sessionStorage.setItem(HISTORY_STACK,JSON.stringify(historyStack, [‘pathname’, ‘search’, ‘key’]),);}});}// 未获取到刷新前的 historyStack,正常初始化if (historyStack.length < 1) {historyStack.push({pathname: window.location.pathname.slice(baseUrl.length),search: window.location.search,key: getPageKey(),});} ```3. 数据回收

  • 基于历史路由栈,因为都是使用 pageKey,从 pageDataMap 中获取所有的 pageKey,再对比 历史路由栈 historyStack 中的 key,得到 historyStack 中不存在的 key ,从 pageDataMap 中清除对应数据* 回收时机:当页面后退或者替换时( POP 和 REPLACE),意味着会有页面数据需要被回收4. 其他

  • 针对我们项目需要,还融合了类似于 redux 的状态管理方案* 额外的对滚动高度的恢复为什么使用 SPA 模拟方案而不直接使用多层 webview?

1.主要原因:应用内存占用。在我们的业务场景中,经常出现较深的路由层级,线上数据的最深层级可达 10,如果使用 webview 会占用较大内存,导致 crash 率飞速上升2.其他原因:大量数据需要同步、端外页面跳转回退的兼容性总结

我们还是更倾向于使用 MPA ,MPA 对于各平台的情况,都有着简单的处理方式来解决路由跳转时的页面状态保存这一问题。

飞速上升2.其他原因:大量数据需要同步、端外页面跳转回退的兼容性总结

我们还是更倾向于使用 MPA ,MPA 对于各平台的情况,都有着简单的处理方式来解决路由跳转时的页面状态保存这一问题。

使用 SPA 并处理路由跳转时的页面状态保存,常见的使用 redux 等状态管理方案,而上文介绍的存取数据的方案提供了另外一种探索,以供参考。

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

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

相关文章

C++学习日记——宏函数和内联函数

学习目标 学习宏函数和内联函数的区别和使用 学习内容 宏函数&#xff1a; 使用宏定义定义出的函数&#xff0c;并不是真正意义上的函数 注意事项&#xff1a; 要保证运算的完整性 使用宏函数存在一定的缺陷 使用场景&#xff1a;频繁调用的短小的函数 优点&#xff1a; 不是…

总结:request获取当前请求路径以及各种参数的方法示例

总结&#xff1a;request获取当前请求路径以及各种参数的方法示例一Java代码中如何获取当前请求路径&#xff0c;以及参数的代码示例&#xff1a;二Java代码中如何获取当前请求路径&#xff0c;以及参数的结果示例&#xff1a;三关于request.getPathInfo() 方法的作用四获取req…

1. 开篇辞和一些SQL语句基本概念

目录 &#xff08;一&#xff09;前言 &#xff08;二&#xff09;SQL概述与历史发展 1. SQL的取名由来以及简述 2. SQL形成与发展的历史 3. SQL的用途 &#xff08;三&#xff09;关于RDBMS简介 &#xff08;一&#xff09;前言 写这个系列的想法在脑海中有一段时间了。其…

<Linux>进程控制

目录 一、进程创建 写实拷贝&#xff1a; 二、进程终止 三、进程等待 一、进程创建 描述一下&#xff0c;fork创建子进程&#xff0c;操作系统都做了什么&#xff1f; fork后父子进程是全部包括之前的代码都共享&#xff1b; fork创建子进程&#xff08;内核数据结构&#x…

【黑马程序员JVM学习笔记】02.内存结构

1.程序计数器 定义&#xff1a; Program Counter Register 程序计数器&#xff08;寄存器&#xff09; 作用&#xff1a; 记住下一条jvm指令的执行地址 getstatic #20 // PrintStream out System.out; astore_1 // -- aload_1 // out.print1n(1); i…

Zstack ZCCC学习笔记

ZCCC 云计算基础技术及解决方案 云计算基础概念 云计算交付方式分类 私有云&#xff1a;数据安全性高、IT基础架构可控制能力强、合规&#xff1b;公有云&#xff1a;初期投入成本低、弹性灵活&#xff1b;混合云&#xff1a;安全、合规、弹性、低成本。 服务模式分类 Ia…

文本检测DB net 实践

github 地址&#xff1a;GitHub - MhLiao/DB: A PyTorch implementation of "Real-time Scene Text Detection with Differentiable Binarization". 其他人的解析&#xff1a;DBNet阅读笔记 - 知乎 DB/DBNet&#xff1a;Real-time Scene Text Detection with Diffe…

未归一化导致Dead ReLU的悲剧

问题描述 笔者在参考http://zh.gluon.ai/chapter_deep-learning-basics/mlp-scratch.html 实现多层感知机的时候&#xff0c;遇到了一个问题 那就是&#xff0c;如果使用ReLU作为激活函数&#xff0c;模型的准确率非常低&#xff08;只有0.1&#xff09; 但是如果把那个网站上…

机器学习入门四

Octave相关资源官网地址下载地址相关语法运算符变量函数系统命令数据操作数据加载数据保存元素操作元素计算绘图和可视化工具绘图实例常用函数控制语句Octave相关资源 官网地址 官方地址 下载地址 下载地址 相关语法 运算符 %&#xff1a;注释~&#xff1a;表示不等于xo…

自学Python 62 使用urllib 包并获取百度搜索关键词中得到链接

Python 使用urllib 包 文章目录Python 使用urllib 包一、urllib 包介绍二、使用urllib.request模块三、使用urllib.parse模块在计算机网络模型中&#xff0c;Socket套接字编程属于底层网络协议开发的内容。虽然说编写网络程序需要从底层开始构建&#xff0c;但是自行处理相关协…

【图像分类】基于HOG特征结合SVM实现图像分类识别附matlab代码

1 内容介绍 ​为了满足人工智能在目标识别方法中的应用需求,需要具备对海量数据进行智能分类、识别、判读的能力.进一步挖掘了目标特性数据库数据,并将基于HOGSVM的目标识别算法应用于红外目标识别过程中.选择采集到的汽车、直升机、飞机、舰船、无人机等目标,并结合HOG算子与…

【Vite 实践】Vite 库模式能满足你吗?或许你需要统一构建

2022 年本人投入了 Vite 的怀抱&#xff0c;开始参与到 Vite 社区中&#xff0c;陆续开发了一些插件。 Vite 秉承了开箱即用&#xff0c;简化配置的思路&#xff0c;确实显著提升了前端开发体验。 但是在类库模式的构建上却有所欠缺&#xff0c;只能处理单个输入和单输入出的…

个人笔记--数据库理论 01 关系模型介绍——基于《数据库系统概念》第七版

关系模式 关系的例子 关系模型是目前广泛应用的数据模型由表的集合构成 例如 IDnamedpt_namesalary11111JAMCS12345 元组 tuple&#xff1a;表中的一行&#xff0c;元素无所谓属性 attribute : 原子的&#xff0c;不可再分的&#xff0c;要有属性域&#xff0c;如上表的nam…

云原生爱好者周刊:延迟加载任意 OCI 镜像 | 2022-09-13

开源项目推荐 SOCI Snapshotter SOCI Snapshotter 是一个 Containerd Snapshotter 插件&#xff0c;可以延迟加载任意 OCI 镜像&#xff0c;不需要 Stargz Snapshotter 一样构建特殊格式的镜像才能延迟加载。 Authentication Proxy 这个项目使用 YARP (Yet Another Reverse…

Git的认识和使用

目录 一、前置准备 二、git简介 三、gitee.com的基本使用 1.创建仓库(私库和公库) 2.创建文件及文件夹 新建文件夹两种方式 ①​ ② 3.删除 删除文件 删除仓库 四、组长组员的git使用 git clone 查看文件 git status git add git commit git push ## 命令行配置 多个…

葡聚糖-MAL/NHS/N3/Alkyne/SH/Biotin/CHO/OPSS/OH

产品名称&#xff1a; 葡聚糖-马来酰亚胺&#xff0c;葡聚糖-MAL&#xff0c;马来酰亚胺功能化葡聚糖 英文名称&#xff1a;Dextran-MAL PEG分子量可选&#xff1a;350,550,750,1k&#xff0c;2k&#xff0c;3.4k&#xff0c;5k&#xff0c;10k&#xff0c;20k&#xff08;可…

[仅需1步]企业微信群机器人[0基础接入][java]

[仅需1步]企业微信群机器人[0基础接入][java]背景介绍使用测试项目背景 公司需要把日常的服务器错误抛到企业微信群中,我正好记录下使用企业微信群机器人… 介绍 企业微信群机器人 应用介绍 企业微信是腾讯微信团队打造的企业通讯与办公工具&#xff0c;具有与微信一致的沟…

医院检验LIS系统源码

医院lis源码 实验室信息管理系统源码 .net检验系统源码 医院系统源码 了解更多源码内容&#xff0c;可私信我。 开发环境&#xff1a;.NET4.0 WPF VS2017或VS2019SQL2016 实验室信息管理系统以条码标本为主线&#xff0c;实现从采集、检测、报告、归档的全程跟踪管理。 支持…

DevOps自动化测试的原则和实践

DevOps是为了在保证高质量的前提下缩短系统变更从提交到部署至生产环境的时间。在对系统进行变更时&#xff0c;质量很重要。高质量才能让业务价值传递到系统干系人。『自动化测试既是提高质量的一种重要手段&#xff0c;也是实施持续测试必需的能力&#xff0c;因此它是DevOps…

修改WebBrowser控件的内核解决方案

首先说一下原理 当下很大浏览器他们都是用了IE的core, 这个core只提供HTML/JS的执行和渲染,并没有给出关于界面和一些特性上的事,所以开发自己浏览器如果基于IE core需要自己完成这些内容。 一张图很好的说明了这个情况,IE浏览器的架构:http://msdn.microsoft.com/en-us/li…