一文读懂浏览器本地存储:Web Storage

news2024/7/6 20:07:59
  • 一、 简介

  • 二、localStorage 和 sessionStorage

    • 2.1、区别

    • 2.2、浏览器兼容性

  • 三、使用说明

    • 3.1、API介绍

    • 3.2、浏览器查看

    • 3.3、监听

  • 四、存储

    • 4.1、存储容量

    • 4.2、存储性能

  • 五、应用

    • 5.1、使用习惯记录

    • 5.2、首次打开提示

    • 5.3、减少重复访问接口

  • 六、总结

一、 简介

浏览器本地存储是指浏览器提供的一种机制,允许 Web 应用程序在浏览器端存储数据,以便在用户下次访问时可以快速获取和使用这些数据。一共两种存储方式:localStorage 和 sessionStorage。下面介绍下两种缓存的特性和在内部平台的一些应用。

二、localStorage 和 sessionStorage

2.1、区别

localStorage 和 sessionStorage 的主要区别是生命周期,具体区别如下:

localStoragesessionStorage
生命周期持久化存储:除非自行删除或清除缓存,否则一直存在会话级别的存储:浏览器标签页或窗口关闭
作用域相同浏览器,同域名,不同标签,不同窗口相同浏览器,同域名,同源窗口
获取方式window.localStoragewindow.sessionStorage
存储容量5M5M

容量限制的目的是防止滥用本地存储空间,导致用户浏览器变慢。

2.2、浏览器兼容性

1)现在的浏览器基本上都是支持这两种 Storage 特性的。各浏览器支持版本如下:

ChromeFirefoxIEOperaSafariAndroidOpera MobileSafari Mobile
localStorage43.5810.542.111iOS 3.2
sessionStorage52810.542.111iOS 3.2

2)如果使用的是老式浏览器,比如Internet Explorer 6、7 或者其他,就需要在使用前检测浏览器是否支持本地存储或者是否被禁用。以 localStorage 为例:

if(window.localStorage){
  alert("浏览器支持 localStorage");
} else {
  alert("浏览器不支持 localStorage");
}

3)某些浏览器版本使用过程中,会出现 Storage 不能正常使用的情况,记得添加 try/catch。以 localStorage 为例:

if(window.localStorage){
  try {
    localStorage.setItem("username", "name");
    alert("浏览器支持 localStorage");
  } catch (e) {
    alert("浏览器支持 localStorage 后不可使用");
  }
} else {
  alert("浏览器不支持 localStorage");
}

三、使用说明

3.1、API介绍

localStorage 和 sessionStorage 提供了相同的方法进行存储、检索和删除。常用的方法如下:

  1. 设置数据:setItem(key, value)

存储的值可以是字符串、数字、布尔、数组和对象。对象和数组必须转换为 string 进行存储。JSON.parse() 和 JSON.stringify() 方法可以将数组、对象等值类型转换为字符串类型,从而存储到 Storage 中;

localStorage.setItem("username", "name"); // "name"
localStorage.setItem("count", 1); // "1"
localStorage.setItem("isOnline", true); // "true"
sessionStorage.setItem("username", "name");
// user 存储时,先使用 JSON 序列化,否则保存的是[object Object]
const user = { "username": "name" };
localStorage.setItem("user", JSON.stringify(user));
sessionStorage.setItem("user", JSON.stringify(user));

eg:数据没有序列化,导致保存的数据异常

图片

  1. 获取数据:getItem(key)

如果 key 对应的 value 获取不到,则返回值是 null;

const usernameLocal = localStorage.getItem("username");
const usernameSession = sessionStorage.getItem("username");
// 获取到的数据为string,使用时反序列化数据
const userLocal = JSON.parse(localStorage.getItem("user"));
const userSession = JSON.parse(sessionStorage.getItem("user"));
  1. 删除数据:removeItem(key);

localStorage.removeItem("username");
sessionStorage.removeItem("username");
  1. 清空数据:clear();

localStorage.clear();
sessionStorage.clear();
  1. 在不确定是否存在 key 的情况下,可以使用 hasOwnProperty() 进行检查;

localStorage.hasOwnProperty("userName"); // true
sessionStorage.hasOwnProperty("userName"); // false
  1. 当然,也可以使用 Object.keys() 查看所有存储数据的键;

Object.keys(localStorage); // ['username']
Object.keys(sessionStorage);

3.2、浏览器查看

本地存储的内容可以在浏览器中直接查看,以 Chrome 为例,按住键盘 F12 进入开发者工具后,选择 Application,然后就能在左边 Storage 列表中找到 localStorage 和 sessionStorgae。

图片

3.3、监听

当存储的数据发生变化时,其他页面通过监听 storage 事件,来获取变更前后的值,以及根据值的变化来处理页面的展示逻辑。

JS 原生监听事件,只能够监听同源非同一个页面中的 storage 事件,如果想监听同一个页面的,需要改写原生方法,抛出自定义事件来监听。具体如下:

  1. 监听同源非同一个页面

直接在其他页面添加监听事件即可。

eg:同域下的 A、B 两个页面,A 修改了 localStorage,B 页面可以监听到 storage 事件。

window.addEventListener("storage", () => {
  // 监听 username 值变化
  if (e.key === "username") {
    console.log("username 旧值:" + e.oldValue + ",新值:" + e.newValue);
  }
})

注:

  • 当两次 setItem 更新的值一样时,监听方法是不会触发的;

  • storage 事件只能监听到 localStorage 的变化。

  1. 监听同一个页面

重写 Storage 的 setItem 事件,同理,也可以监听删除事件 removeItem 和获取事件 getItem。

(() => {
  const originalSetItem = localStorage.setItem;
  // 重写 setItem 函数
  localStorage.setItem = function (key, val) {
    let event = new Event("setItemEvent");
    event.key = key;
    event.newValue = val;
    window.dispatchEvent(event);
    originalSetItem.apply(this, arguments);
  };
})();

window.addEventListener("setItemEvent", function (e) {
  // 监听 username 值变化
  if (e.key === "username") {
    const oldValue = localStorage.getItem(e.key);
    console.log("username 旧值:" + oldValue + ",新值:" + e.newValue);
  }
});

四、存储

浏览器默认能够存储 5M 的数据,但实际上,浏览器并不会为其分配特定的存储空间,而是根据当前浏览器的空闲空间来判断能够分配多少存储空间。

4.1、存储容量

可以使用 Storage 的 length 属性,对存储容量进行测算,以 localStorage 为例:

let str = "0123456789";
let temp = "";
// 先生成一个 10KB 的字符串
while (str.length !== 10240) {
  str = str + "0123456789";
}
// 清空
localStorage.clear();
// 计算总量
const computedTotal = () => {
  return new Promise((resolve) => {
    // 往 localStorage 中累积存储 10KB
    const timer = setInterval(() => {
      try {
        localStorage.setItem("temp", temp);
      } catch (e) {
        // 报错说明超出最大存储
        resolve(temp.length / 1024);
        clearInterval(timer);
        // 统计完记得清空
        localStorage.clear();
      }
      temp += str;
    }, 0);
  });
};
// 计算使用量
const computedUse = () => {
  let cache = 0;
  for (let key in localStorage) {
    if (localStorage.hasOwnProperty(key)) {
      cache += localStorage.getItem(key).length;
    }
  }
  return (cache / 1024).toFixed(2);
};

(async () => {
  const total = await computedTotal();
  let use = "0123456789";
  for (let i = 0; i < 1000; i++) {
    use += "0123456789";
  }
  localStorage.setItem("use", use);
  const useCache = computedUse();

  console.log(`最大容量${total}KB`);
  console.log(`已用${useCache}KB`);
  console.log(`剩余可用容量${total - useCache}KB`);
})();

可见在 Chrome 浏览器下,localStorage 有 5M 容量。

图片

4.2、存储性能

在某些特殊场景下,需要存储大数据,为了更好的利用 Storage 的存储空间,可以采取以下解决方案,但不应该过于频繁地将大量数据存储在 Storage 中,因为在写入数据时,会对整个页面进行阻塞(不推荐这种方式)。

  1. 压缩数据

可以使用数据压缩库对 Storage 中的数据进行压缩,从而减小数据占用的存储空间。

eg:使用 lz-string 库的 compress() 函数将数据进行压缩,并将压缩后的数据存储到 localStorage 中。

const LZString = require("lz-string");
const data = "This is a test message";
// 压缩
const compressedData = LZString.compress(data);
localStorage.setItem("test", compressedData);
// 解压
const decompressedData = LZString.decompress(localStorage.getItem("test"));
  1. 分割数据

将大的数据分割成多个小的片段存储到 Storage 中,从而减小单个数据占用的存储空间。

eg:将用户数据分割为单项存储到 localStorage 中。

for (const key in userInfo) {
  localStorage.setItem(key, userInfo[key]);
}

图片

  1. 取消不必要的数据存储

可以在代码中取消一些不必要的数据存储,从而减小占用空间。

eg:只存储用到的用户名、公司主体和后端所在环境字段信息。

for (const key in userInfo) {
  if (["userName", "legalEntityName", "isOnline"].includes(key)) {
    localStorage.setItem(key, userInfo[key]);
  }
}

图片

  1. 设置过期时间

localStorage 是不支持过期时间的,在存储信息过多后,会拖慢浏览器速度,也会因为浏览器存储容量不够而报错,可以封装一层逻辑来实现设置过期时间,以达到清理的目的。

// 设置
function set(key, value){
  const time = new Date().getTime(); //获取当前时间
  localStorage.setItem(key, JSON.stringify({value, time})); //转换成json字符串
}
// 获取
function get(key, exp){
  // exp 过期时间
  const value = localStorage.getItem(key); 
  const valueJson = JSON.parse(value); 
  //当前时间 - 存储的创建时间 > 过期时间
  if(new Date().getTime() - valueJson.time > exp){
    console.log("expires"); //提示过期
  } else {
    console.log("value:" + valueJson.value);
  }
}

五、应用

5.1、使用习惯记录

用来缓存一些筛选项数据,保存用户习惯信息,起到避免多次重复操作的作用。

eg:在 beetle 工程列表中,展示了自已权限下所有 beetle 的项目,对于参与项目多和参与项目少的人,操作习惯是不同的,由此,记录收藏功能状态解决了这一问题,让筛选项记住用户选择,方便下次使用。

图片

图片

在开发使用中,直接获取 localStorage.getItem('isFavor') 作为默认值展示,切换后使用 localStorage.setItem() 方法更新保存内容。

// 获取
const isFavor = localStorage.getItem('isFavor');
this.state = {
  isFavor: isFavor !== null ? Number(isFavor) : EngineeringTypeEnum.FAVOR,
};
// 展示默认值
<Form.Item name='isFavor' initialValue={this.state.isFavor}>
  <Select
    placeholder='筛选收藏的工程'
    onChange={(e) => this.changeFavor(e)}
    {...searchSmallFormProps}
  >
      {EngineeringTypeEnum.property.map(e => (<Option key={e.id} value={e.id}>{e.name}</Option>))}
  </Select>
</Form.Item>
// 变更
changeFavor = (e) => {
  localStorage.setItem('isFavor', e);
  this.setState({ isFavor: e });
};

5.2、首次打开提示

用来缓存用户导览,尤其是只需要出现一次的操作说明弹窗等。

eg:当第一次或者清空缓存后登录,页面需要出现操作指南和用户手册的弹窗说明。

图片

在开发使用中,注意存储的数据类型为 string,转成布尔值是为了在插件中方便控制弹窗的显示隐藏。

// 获取
const operationVisible = localStorage.getItem('operationVisible');
this.state = {
  operationVisible: operationVisible === null || operationVisible === 'true' ? true : false,
};
// 控制展示
<Modal
  title='操作指南'
  open={this.state.operationVisible}
  onCancel={() => { 
    this.setState({ operationVisible: false }); 
    localStorage.setItem('operationVisible', false); 
  }}
  footer={null}
  destroyOnClose={true}
>
  <Divider orientation='left'>动作</Divider>
  <p>接口 》 用例 》 用例集,3级结构满足不了后续的使用,因此增加【动作】这一层级,【动作】是接口测试的最小单元,多个【动作】可以组合成一个用例,多个用例可以聚合为用例集;</p>
  <Image src={OperationGuidePng} preview={false} />
</Modal>

5.3、减少重复访问接口

在浏览页面时,会遇到一些经常访问但返回数据不更新的接口,这种特别适合用做页面缓存,只在页面打开的时候访问一次,其他时间获取缓存数据即可。

eg:在我们的一些内部系统中,用户信息是每个页面都要用到的,尤其是 userId 字段,会与每个获取数据接口挂钩,但这个数据是不会变的,一直请求是没有意义的,为减少接口的访问次数,可以将主要数据缓存在 localStorage 内,方便其他接口获取。

六、总结

希望通过此篇文章,可以让大家了解 Web Storage 在浏览器数据存储和读取的相关操作,以及相关事件和限制。

它可以用于保存用户的偏好设置、表单数据等,在开发中使用可以方便的存储和读取数据,提高用户体验。当然,在使用时需要特别注意它的限制,以及在存储、读取和删除数据过程中的错误处理。

关于作者

刘筱雨,转转工程效率组内成员,主要负责公司内部公共系统前端项目。

> 转转研发中心及业界小伙伴们的技术学习交流平台,定期分享一线的实战经验及业界前沿的技术话题。

> 关注公众号「转转技术」(综合性)、「大转转FE」(专注于FE)、「转转QA」(专注于QA),更多干货实践,欢迎交流分享~

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

在 3ds Max 中使用相机映射将静止图像转换为实时素材

推荐&#xff1a; NSDT场景编辑器 助你快速搭建可二次开发的3D应用场景 1. 在 Photoshop 中准备图像 步骤 1 这是我将在教程中使用的静止图像。 这是我的静态相机纸箱的快照。 静止图像 步骤 2 打开 Photoshop。将图像导入 Photoshop。 打开 Photoshop 步骤 3 单击套索工…

计算机网络基础(静态路由,动态路由,公网IP,私网IP,NAT技术)

文章目录 一&#xff1a;静态路由和动态路由二&#xff1a;静态路由的配置路由信息的方式演示三&#xff1a;默认路由四&#xff1a;公网IP和私网IP和NAT技术的基本理解 一&#xff1a;静态路由和动态路由 在说静态路由和动态路由前&#xff0c;我们需要来了解一下&#xff0…

近2年入侵13家电信公司的幕后黑手浮出水面

10月20日&#xff0c;网络安全公司赛门铁克刚披露了一个针对南亚电信公司的神秘APT&#xff08;高级持续威胁&#xff09;组织&#xff0c;一个名为 LightBasin 的黑客组织被确定为针对电信行业发起一系列攻击的幕后黑手&#xff0c;其目标是从移动通信基础设施中收集“高度特定…

【iOS】GCD深入学习

关于GCD和队列的简单介绍请看&#xff1a;【iOS】GCD学习 本篇主要介绍GCD中的方法。 栅栏方法:dispatch_barrier_async 我们有时候需要异步执行两组操作&#xff0c;而且第一组操作执行完之后&#xff0c;才能开始执行第二组操作&#xff0c;当然操作组里也可以包含一个或者…

JVM之三大垃圾回收算法

文章目录 前言一、复制算法二、标记清除三、标记整理 前言 提示&#xff1a;这里可以添加本文要记录的大概内容&#xff1a; 例如&#xff1a;随着人工智能的不断发展&#xff0c;机器学习这门技术也越来越重要&#xff0c;很多人都开启了学习机器学习&#xff0c;本文就介绍…

Django实现音乐网站 ⑶

使用Python Django框架制作一个音乐网站&#xff0c;在系列文章2的基础上继续开发&#xff0c;本篇主要是后台单曲、专辑、首页轮播图表模块开发。 目录 后台单曲、专辑表模块开发 表结构设计 单曲表&#xff08;singe&#xff09;结构 专辑表&#xff08;album&#xff09…

安达发|APS生产派单系统对数字化工厂有哪些影响和作用

数字化工厂是当今制造业的热门话题&#xff0c;而APS软件则是这一领域的颠覆者。它以其独特的影响和作用&#xff0c;给制造业带来了巨大的改变。让我们一起来看看APS软件对数字化工厂有哪些影响和作用吧&#xff01; 提高生产效率的神器 1.APS软件作为数字化工厂的核心系统&a…

Jenkins工具系列 —— 启动 Jenkins 服务报错

错误显示 apt-get 安装 Jenkins 后&#xff0c;自动启动 Jenkins 服务报错。 排查原因 直接运行jenkins命令 发现具体报错log&#xff1a;Failed to start Jetty或Failed to bind to 0.0.0.0/0.0.0.0:8080或Address already in use 说明&#xff1a;这里提示的是8080端口号…

权威认可|云畅科技再次入选中国信通院「高质量数字化转型产品及服务全景图」

7月27日&#xff0c;由中国信通院主办的2023数字生态发展大会暨中国信通院“铸基计划”年中会议在北京成功召开。 会上&#xff0c;中国信通院重磅发布了「高质量数字化转型产品及服务全景图&#xff08;2023&#xff09;」&#xff0c;云畅科技凭借其自研产品「万应低代码」在…

【编程语言 · C语言 · 共用体指针】

【编程语言 C语言 共用体指针】https://mp.weixin.qq.com/s?__bizMzg4NTE5MDAzOA&mid2247491538&idx1&sne1941bffaa2b85d4a7932fa94bccc84d&chksmcfade32bf8da6a3d5fc729b29452259127a7ff63efd2ad77607b0d2f2c72250b86e1841e76d3&payreadticketHLky0Bq4…

从引入并集成多LLM到发布自研模型,RPA与LLM的融合进度怎样了?

RPA厂商对于大语言模型&#xff08;LLM&#xff0c;Large Language Model&#xff09;的应用&#xff0c;比大家想象的还要早一些。 毕竟&#xff0c;2019年兴起的这一波RPA热&#xff0c;背后都是因为AI技术。没有AI技术与RPA的融合&#xff0c;也就没有现在的RPA。 为了全力…

tinkerCAD案例:31. 3D 基元形状简介

tinkerCAD案例&#xff1a;31. 3D 基元形状简介 1 将一个想法从头脑带到现实世界是一次令人兴奋的冒险。在 Tinkercad 中&#xff0c;这将从一个新的设计开始。 在新设计中&#xff0c;简单的原始形状可以通过不同的方式组合成更复杂的形状。 在这个项目中&#xff0c;你将探索…

JNPF-一个真正可拓展的低代码全栈框架

一、前言 尽管现在越来越多的人开始对低代码开发感兴趣&#xff0c;但已有低代码方案的一些局限性仍然让大家有所保留。其中最常见的担忧莫过于低代码缺乏灵活性以及容易被厂商锁定。 显然这样的担忧是合理的&#xff0c;因为大家都不希望在实现特定功能的时候才发现低代码平台…

在win10下安装verilator

主要参考文章 Verilator简介及其下载安装卸载_徐晓康的博客的博客-CSDN博客https://blog.csdn.net/weixin_42837669/article/details/114505364上面的文章可以解决大部分问题,但是可能是方案有些老了,已经安装最新的版本,下面对最新的版本安装提供解决方案 一 预备工作 安…

python处理Excel常用模块:xlrd/xlwt/xlsxwriter/openpyxl/pandas/win32.com差异比较汇总以及常用使用方法

一、各模块优缺点比较 模块xlrdxlwt xlsxwriteropenpyxlpandaswin32.com读YESNONOYESYESYES写NOYESYESYESYESYES速度快快快比较快比较快超慢功能弱弱强一般一般超强执行速度 支持处理对xls、xlsx、xlsm&#xff0c;效率较高 只写&#xff0c;并且写入耗时较小&#xff0c;效率…

x 的平方根——力扣69

文章目录 题目描述法一 二分查找 题目描述 法一 二分查找 int mySqrt(int x){int l0, rx, ans-1;while(l<r){int mid (lr)/2;if((long long)mid*mid<x){ansmid;lmid1;} else {rmid-1;}}return ans;}

“用户登录”测试用例总结

前言&#xff1a;作为测试工程师&#xff0c;你的目标是要保证系统在各种应用场景下的功能是符合设计要求的&#xff0c;所以你需要考虑的测试用例就需要更多、更全面。鉴于面试中经常会问“”如何测试用户登录“”&#xff0c;我们利用等价类划分、边界值分析等设计一些测试用…

initDB时,数据库的ip地址、用户名、密码全都正确,但是连不上 oracle

选择解决方案。右击选中属性&#xff0c;修改生成的目标平台&#xff1a;

那到底是嵌入式累还是程序员累?

累的定义有很多&#xff0c;有体力上的&#xff0c;精力上的&#xff0c;也有心理上的&#xff0c;而心理的累才是真的累。 先来说说体力上的&#xff0c;现在不像60年代&#xff0c;上山砍柴扛几公里来挣钱&#xff0c;时代在进步&#xff0c;逐渐从体力到脑力转变。 以前那…

在开酒吧前要知道的9条干货

最近总结了一下很多人开酒吧期间遇到的问题&#xff0c;整理出了一些干货&#xff0c;希望能对有开酒吧想法的小白们有所帮助。1、开酒吧之前要去做好市场调研和风险评估&#xff0c;这能帮助你大致了解所选地区的情况2、开酒吧之前要做好前期亏损的准备&#xff0c;准备好一定…