Web端IM聊天消息该不该用浏览器本地存储?一文即懂!

简介: 鉴于目前浏览器技术的进步(主要是HTML5的普及),在Web网页端IM聊天应用的技术选型阶段,很多开发者都会纠结到底该不该像原生移动端IM那样将聊天记录缓存在浏览器的本地,还是像传统Web端即时通讯那样继续存储在服务端?本文将为你简洁明了地讲清楚浏览器本地存储技术(Web Storage),然后你就知道到底该怎么选择了。

本文由转转技术团队刘筱雨分享,原题“一文读懂浏览器本地存储:Web Storage”,下文进行了排版和内容优化。

1、引言

鉴于目前浏览器技术的进步(主要是HTML5的普及),在Web网页端IM聊天应用的技术选型阶段,很多开发者都会纠结到底该不该像原生移动端IM那样将聊天记录缓存在浏览器的本地,还是像传统Web端即时通讯那样继续存储在服务端?本文将为你简洁明了地讲清楚浏览器本地存储技术(Web Storage),然后你就知道到底该怎么选择了。

浏览器本地存储是指浏览器提供的一种机制,允许 Web 应用程序在浏览器端存储数据,以便在用户下次访问时可以快速获取和使用这些数据。一共两种存储方式:localStorage 和 sessionStorage,本文将主要围绕这两种技术来进行总结。

 

 

技术交流:

- 移动端IM开发入门文章:《新手入门一篇就够:从零开发移动端IM

- 开源IM框架源码:https://github.com/JackJiang2011/MobileIMSDK备用地址点此

本文已同步发布于:http://www.52im.net/thread-4745-1-1.html

2、初识浏览器本地存储(localStorage和sessionStorage)

2.1区别

localStorage 和 sessionStorage 的主要区别是生命周期。

具体区别如下:

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

2.2浏览器兼容性

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

各浏览器支持版本如下:

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、基本用法演示

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:数据没有序列化,导致保存的数据异常:

2)获取数据: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"));

3)删除数据:removeItem(key);

localStorage.removeItem("username");

sessionStorage.removeItem("username");

4)清空数据:clear();

localStorage.clear();

sessionStorage.clear();

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

localStorage.hasOwnProperty("userName"); // true

sessionStorage.hasOwnProperty("userName"); // false

6)当然,也可以使用 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);

 }

})

注:

  • 1)当两次 setItem 更新的值一样时,监听方法是不会触发的;
  • 2)storage 事件只能监听到 localStorage 的变化。

2)监听同一个页面:

重写 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);

 }

});

4、存储容量上限到底是不是5MB?

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

可以使用 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 容量:

5、用好本地存储的一些建议

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

5.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"));

5.2分割数据

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

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

for (const key in userInfo) {

 localStorage.setItem(key, userInfo[key]);

}

5.3取消不必要的数据存储

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

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

for (const key in userInfo) {

 if (["userName", "legalEntityName", "isOnline"].includes(key)) {

   localStorage.setItem(key, userInfo[key]);

 }

}

5.4设置过期时间

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);

 }

}

6、实践应用中的案例参考

6.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 });

};

6.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>

6.3 减少重复访问接口

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

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

7、本文小结

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

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

8、参考资料

[1] 新手入门贴:史上最全Web端即时通讯技术原理详解

[2] Web端即时通讯技术盘点:短轮询、Comet、Websocket、SSE

[3] 一文读懂前端技术演进:盘点Web前端20年的技术变迁史

[4] Web端即时通讯基础知识补课:一文搞懂跨域的所有问题!

[5] Web端即时通讯实践干货:如何让你的WebSocket断网重连更快速?

[6] WebSocket从入门到精通,半小时就够!

[7] WebSocket硬核入门:200行代码,教你徒手撸一个WebSocket服务器

[8] 长连接网关技术专题(四):爱奇艺WebSocket实时推送网关技术实践

[9] 网页端IM通信技术快速入门:短轮询、长轮询、SSE、WebSocket

[10] 搞懂现代Web端即时通讯技术一文就够:WebSocket、socket.io、SSE

[11] IM跨平台技术学习(一):快速了解新一代跨平台桌面技术——Electron

[12] Wasm在即时通讯IM场景下的Web端应用性能提升初探

[13] 一套海量在线用户的移动端IM架构设计实践分享(含详细图文)

[14] 一套亿级用户的IM架构技术干货(上篇):整体架构、服务拆分等

[15] 一套亿级用户的IM架构技术干货(下篇):可靠性、有序性、弱网优化等

[16] 从新手到专家:如何设计一套亿级消息量的分布式IM系统

[17] 新手入门一篇就够:从零开发移动端IM

(本文已同步发布于:http://www.52im.net/thread-4745-1-1.html

目录
相关文章
|
5月前
|
Web App开发 iOS开发
Web 浏览器
【8月更文挑战第27天】Web 浏览器。
67 2
|
2月前
|
人工智能 前端开发 计算机视觉
Inpaint-Web:纯浏览器端实现的开源图像处理工具
在刷短视频时,常看到情侣在景区拍照被路人“抢镜”,男朋友用手机将路人“P”掉,既贴心又有趣。最近我发现了一个纯前端实现的开源项目——inpaint-web,可在浏览器端删除照片中的部分内容,非常酷。该项目基于 WebGPU 和 WASM 技术,支持图像修复与放大,已在 GitHub 上获得 5.1k Star。项目地址:[GitHub](https://github.com/lxfater/inpaint-web)。
71 3
 Inpaint-Web:纯浏览器端实现的开源图像处理工具
|
2月前
|
Rust 前端开发 JavaScript
Wasm在即时通讯IM场景下的Web端应用性能提升初探
简单的来说,Wasm就是使用C/C++/Rust等语言编写的代码,经过编译后得到汇编指令,再通过JavaScript相关API将文件加载到Web容器中(即运行在Web容器中的汇编代码)。Wasm是一种可移植、体积小、加载快速的二进制格式,可以将各种编程语言的代码编译成Wasm模块,这些模块可以在现代浏览器中直接运行。尤其在涉及到GPU或CPU计算时优势相对比较明显。
40 0
|
3月前
|
Web App开发 前端开发 网络性能优化
Web网页端IM产品RainbowChat-Web的v7.2版已发布
RainbowChat-Web是一套Web网页端IM系统,是RainbowChat的姊妹系统(RainbowChat是一套基于开源IM聊天框架 MobileIMSDK (Github地址) 的产品级移动端IM系统)。
47 1
|
5月前
|
机器学习/深度学习 存储 前端开发
实战揭秘:如何借助TensorFlow.js的强大力量,轻松将高效能的机器学习模型无缝集成到Web浏览器中,从而打造智能化的前端应用并优化用户体验
【8月更文挑战第31天】将机器学习模型集成到Web应用中,可让用户在浏览器内体验智能化功能。TensorFlow.js作为在客户端浏览器中运行的库,提供了强大支持。本文通过问答形式详细介绍如何使用TensorFlow.js将机器学习模型带入Web浏览器,并通过具体示例代码展示最佳实践。首先,需在HTML文件中引入TensorFlow.js库;接着,可通过加载预训练模型如MobileNet实现图像分类;然后,编写代码处理图像识别并显示结果;此外,还介绍了如何训练自定义模型及优化模型性能的方法,包括模型量化、剪枝和压缩等。
77 1
|
5月前
|
存储 JavaScript 程序员
Vue学习之---浏览器本地存储(8/17)
这篇文章介绍了Vue中浏览器本地存储的使用方法,包括基础知识、localStorage和sessionStorage的代码实例及其测试效果,并提供了相关的API和操作示例。
Vue学习之---浏览器本地存储(8/17)
|
5月前
|
存储 前端开发 JavaScript
揭秘!JavaScript本地存储的四大绝技:从Cookie到IndexedDB,让你的Web应用秒变数据存储高手,轻松应对各种挑战!
【8月更文挑战第4天】JavaScript为核心前端技术,提供多样本地存储方案以优化用户体验与减少服务器负载。首先,Cookie虽用于基本数据如登录状态,但受大小限制及安全性影响。接着,Web Storage中的LocalStorage持久存储不变数据,SessionStorage则限于单次会话。更进一步,IndexedDB作为全面数据库解决方案,支持复杂数据操作但使用较复杂。每种方式根据应用需求各有优势。
83 9
|
5月前
|
API UED 开发者
如何在Uno Platform中轻松实现流畅动画效果——从基础到优化,全方位打造用户友好的动态交互体验!
【8月更文挑战第31天】在开发跨平台应用时,确保用户界面流畅且具吸引力至关重要。Uno Platform 作为多端统一的开发框架,不仅支持跨系统应用开发,还能通过优化实现流畅动画,增强用户体验。本文探讨了Uno Platform中实现流畅动画的多个方面,包括动画基础、性能优化、实践技巧及问题排查,帮助开发者掌握具体优化策略,提升应用质量与用户满意度。通过合理利用故事板、减少布局复杂性、使用硬件加速等技术,结合异步方法与预设缓存技巧,开发者能够创建美观且流畅的动画效果。
93 0
|
6月前
|
机器人 Shell 开发者
`roslibpy`是一个Python库,它允许非ROS(Robot Operating System)环境(如Web浏览器、移动应用等)与ROS环境进行交互。通过使用`roslibpy`,开发者可以编写Python代码来远程控制ROS节点,发布和订阅话题,以及调用服务。
`roslibpy`是一个Python库,它允许非ROS(Robot Operating System)环境(如Web浏览器、移动应用等)与ROS环境进行交互。通过使用`roslibpy`,开发者可以编写Python代码来远程控制ROS节点,发布和订阅话题,以及调用服务。
|
5月前
|
存储 移动开发 编解码
一文读懂Web Codecs API:浏览器背后的媒体魔术师
一文读懂Web Codecs API:浏览器背后的媒体魔术师
54 0