浏览器持久化存储那些事

现在前后端分离是比较流行的开发方案,在 SPA 的开发过程中难免需要存储一些数据到浏览器,比如认证判断的 Token 。现代常用浏览器的本地持久存储方案基本分为CookieLocalStorage(sessionStorage)IndexedDB。本文就不讨论 Cookie了。

一、LocalStorage(sessionStorage)的概念

LocalStorage 是 HTML5加入的一个新特性,从命名就知道其目的是作为本地存储来使用,解决了 Cookie 存储空间不足的问题,一般 LocalStorage 在浏览器的存储空间是5MB,而 Cookie中每条 cookie 大约是4KBSessionStorageLocalStorage 唯一的区别是:SessionStorage 在会话结束时就会被 清空数据,而LocalStorage 可以持久存储。

LocalStorage的优劣

1、LocalStorage存储空间明显提升,可以存储较多的数据;

2、LocalStorage可以长久存储数据,只要存储一次就可以长久保留在本地,除非主动清空数据;

3、LocalStorageHTML5的新特性,只会被现代浏览器支持,IE8以下的IE浏览器就不支持该特性;

4、LocalStorage 只能存储字符串

5、在隐私模式下,不能使用 LocalStorage;

6、LocalStorage 不会被爬虫嗅探并抓取;

7、LocalStorage的使用是遵循同源策略。

LocalStorage的使用方法

1、在使用 LocalStorage之前需要检测当前浏览器是否支持。

1
2
3
4
5
if(!window.localStorage){
console.log("浏览器 不支持LocalStorage");
return ;
}
//正常使用 LocalStorage

2、LocalStorage 的数据写入可以有3种方式,其实都是键值对操作:

1
2
3
4
5
6
7
8
9
10
11
12
if(!window.localStorage){
console.log("浏览器 不支持LocalStorage");
return ;
}
//正常使用 LocalStorage
const storage=window.localStorage;

storage["count"]=1;

storage.name='qimajiang';
//写入存储进去的搜索字符串类型
storage.setItem("other",3);

一般使用 storage.setItem这种方式比较好。

3、LocalStorage 的数据读取

1
2
3
4
5
6
7
8
9
10
11
12
if(!window.localStorage){
console.log("浏览器 不支持LocalStorage");
return ;
}
//正常使用 LocalStorage
const storage=window.localStorage;

console.log(storage['count'])//1

console.log(storage.name);//'qimajiang'

console.log(storage.getItem("other");)//3

一般使用 storage.getItem这种方式比较好。

4、LocalStorage 的其他数据操作

删除某个键值:window.localStorage.removeItem('key')

清空当前网站的LocalStoragewindow.localStorage.clear()


一般使用 LocalStorage 来存储简单的数据(字符串)就可以满足了,但在日益复杂的应用开发中,难免需要存储复杂的结构化数据,而且有可能存储的数据超过5MB,这时我们就需要考虑使用 IndexedDB


二、IndexedDB 的概念

1、IndexedDB顾名思义是一种内置在浏览器中数据库,而且是一种非关系型的数据库,即不需要编写SQL语句去操作数据库,而且存储的数据格式是JSON

2、IndexedDB不像我们平时服务器上使用的 NoSQL 数据库,IndexedDB没有表的概念,在IndexedDB中是叫object store,其实平时就可以把object store看做数据表。

3、IndexedDB的每次操作都是一个事务,每一个对数据库操作是在一个事务的上下文中执行的。

4、IndexedDB的每次数据库操作都需要先打款object store,再执行指定的操作,不能一直打开某个object store

5、IndexedDB的所有操作都是异步的。

三、IndexedDBReact 中的实践

因为我目前主要专注React项目的开发,而且IndexedDB的原生 api 有点奇怪,在我的项目中我是使用 Dexie这个基于IndexedDBapi 封装的操作库。后面就阐述一下如何使用Dexie进行IndexedDB的数据操作。

1、安装npm i -S dexie

2、在项目根目录建立一个db文件夹,新建一个文件db.js,在db.js中定义数据库;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

import Dexie from 'dexie';
const db = new Dexie('HXDB');
//系统所有权限列表的数据
const SYSTEM_ALL_PERMISSIONS_SCHEMA = 'code, name, module';

export const DEXIE_STORES_1 = {

SYSTEM_ALL_PERMISSIONS: SYSTEM_ALL_PERMISSIONS_SCHEMA
};
db.version(2).stores(DEXIE_STORES_1);
db.version(1).stores(DEXIE_STORES_1);//定义版本
db.open();//打开数据库

export default db;//模块化导出

IndexedDB使用 version 来区分不同版本号,因为不同的版本可能使用不同的 store 结构,每次更改 store 的结构,需要增加 version 的值,而且旧的版本需要保留。

3、数据操作

db目录中新建operations.js文件,定义一些常用的数据操作方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
import db from 'db/db';

/**
* Created By xun on 2018-08-16 17:14.
* Description: operations
*/
export const getDBAllDataArray = async tableName => {
const myTable = await db[tableName];

return myTable.toArray();
};

export const getDBDataByKey = (tableName, key) => {
return db.table(tableName).get(key);
};
// 单个添加
export const addDBData = (tableName, values) => {
return db.table(tableName).add(values);
};

// 批量添加
export const addDBDataBatch = (tableName, values) => {
return db.table(tableName).bulkAdd(values);
};

//存在就更新, 否则添加
export const softInsertDBData = async (tableName, values, key = null) => {
if (!key) {
return addDBData(tableName, values);
}
const existed = await getDBDataByKey(tableName, key);
if (existed) {
return db.table(tableName).update(key, values);
} else {
return addDBData(tableName, values);
}
};

//删除
export const deleteDBDataByKey = async (tableName, key) => {
return db.table(tableName).delete(key);
};

//清空
export const clearAllDBData = async tableName => {
return db.table(tableName).clear();
};

若要增加其他特殊的数据操作,就直接在operations.js文件中增加方法即可,避免重复的编码。


IndexedDB理论上的存储空间是电脑硬盘容量的一般,所以存储空间超级充足了。


四、解决IndexedDB的异步问题

前文有提到IndexedDB的所有数据操作都是异步的,在一些情况下我们需要模拟同步操作,例如把菜单的配置放在IndexedDB中,那么每次从IndexedDB获取到菜单定义数据后需要回调数据显示,有点麻烦。我在项目中使用mobx状态管理器来同步所有的IndexedDB数据,只有不刷新页面,就可以同步操作IndexedDB数据了。目前都是读取操作,数据都是从服务器配置好的,存储在IndexedDB是不需要每次从服务器获取。

一般在更新IndexedDB中数据的时候,就更新mobx中的 store数据。

五、总结

浏览器的本次存储在现在 web 应用开发中还是比较重要的,掌握了这些前端本地存储方法有利于提升应用以及应用共享数据。