在项目开发过程中,前端需要存储大量的数据。cookie, localstorage 都有存储长度限制。
表格一览
特性 | cookie | localStorage | sessionStorage | indexedDB |
---|---|---|---|---|
数据生命周期 | 一般由服务器生成,可以设置过期时间;前端采用和js-cookie等组件也可以生成 | 除非被清理,否则一直存在;浏览器关闭还会保存在本地,但是不支持跨浏览器 | 页面关闭就清理刷新依然存在,不支持跨页面交互 | 除非被清理,否则一直存在 |
数据存储大小 | 4K | 5M | 5M | 不限制大小 |
与服务端通信 | 每次都会携带在请求的header 中,对于请求性能有影响;同时由于请求中都带有,所以也容易出现安全问题 | 不参与 | 不参与 | 不参与 |
特点 | 字符串键值对在本地存储数据 | 字符串键值对在本地存储数据 | 字符串键值对在本地存储数据 | IndexedDB 是一个非关系型数据库(不支持通过 SQL 语句操作)。可以存储大量数据,提供接口来查询,还可以建立索引,这些都是其他存储方案无法提供的能力。 |
需要一个存储容量大,支持搜索和自定义索引的前端存储方案,就选用了 。
caniuse上查看 indexedDB 支持情况,目前浏览器支持情况良好。
caniuse.com/?search=ind…
IndexedDB 属于非关系型数据库。(不支持SQL查询)
/*
*@databaseName 数据仓库的名字
*@version 数据仓库的版本
*@databaseName 数据仓库的名字
*/
var request = window.indexedDB.open('group', 1);
/*
*数据仓库打开失败
*/
request.onerror = function(error) {
console.log('IndexedDB 打开失败', error);
};
/*
*数据仓库打开成功
*/
request.onsuccess = function(res) {
console.log('IndexedDB 打开成功', res);
db = res.target.result;
};
/*
*数据仓库升级事件(第一次新建库是也会触发,因为数据仓库从无到有算是升级了一次)
*/
request.onupgradeneeded = function(res) {
console.log('IndexedDB 升级成功', res);
db = res.target.result;
db_table = db.createObjectStore('group', { keyPath: 'id' });
db_table.createIndex('indexName', 'name', { unique: false });
};
/*
*新建事务
*@params 数据仓库的数组
*@params 写入模式
*/
var store = db.transaction(['group'], 'readwrite').objectStore('group');
/*
*add方法添加数据
*@params 需要添加的数据信息
*/
var request = store.add({
id: new Date().getTime(),
name: '王二',
age: 12,
email: '[email protected]',
});
/*
*添加成功
*/
request.onsuccess = function(event) {
console.log('数据添加成功', event);
};
/*
*添加失败
*/
request.onerror = function(event) {
console.log('数据添加失败', event);
};
/*
*新建事务
*@params 数据仓库的数组
*/
var store = db.transaction(['group']).objectStore('group');
/*
*get方法获取数据
*@params 数据的主键
*/
var request = store.get(1678664831491);
/*
*获取成功
*/
request.onsuccess = function(event) {
if (event.target.result) {
console.log('数据获取成功', event.target.result);
} else {
console.log('未获取到数据');
}
};
/*
*获取失败
*/
request.onerror = function(event) {
console.log('数据获取失败', event);
};
/*
*新建事务
*@params 数据仓库的数组
*@params 写入模式
*/
var store = db.transaction(['group'], 'readwrite').objectStore('group');
/*
*put方法根据主键更新数据
*@params 数据的主键
*/
var request = store.put({
id: 1678664831491,
name: '张一' + Math.random(),
age: 24,
email: '[email protected]',
});
/*
*更新成功
*/
request.onsuccess = function(event) {
console.log('数据更新成功', event);
};
/*
*更新失败
*/
request.onerror = function(event) {
console.log('数据更新失败', event);
};
未加 readwrite, 会抛错,修改数据失败
/*
*新建事务
*@params 数据仓库的数组
*/
var store = db.transaction(['group'], 'readwrite').objectStore('group');
/*
*delete方法删除数据
*@params 数据的主键
*/
var request = store.delete(1678664831491);
/*
*删除成功
*/
request.onsuccess = function (event) {
console.log('数据删除成功',event);
};
/*
*删除失败
*/
request.onerror = function (event) {
console.log('数据删除失败',event);
};
/*
*新建事务
*@params 数据仓库的数组
*/
var store = db.transaction(['group']).objectStore('group');
/*
*index方法获取索引对象
*get方法获取数据
*@params 数据的索引
*/
var request = store.index('indexName').get('张四');
/*
*获取成功
*/
request.onsuccess = function (event) {
console.log('通过索引获取数据成功',event.target.result);
};
/*
*获取失败
*/
request.onerror = function (event) {
console.log('通过索引获取数据失败',event);
};
var store = db.transaction(['group']).objectStore('group');
var request = store.getAll();
/*
*更新成功
*/
request.onsuccess = function(event) {
console.log('indexedDB getAll:', event.target.result);
};
/*
*更新失败
*/
request.onerror = function(event) {
console.log('indexedDB getAll:', event);
};
首先让我们 来了解 IDBKeyRange
的API
www.w3cschool.cn/javascript_…
var store = db.transaction(['group']).objectStore('group');
// 获取id名称小于当前时间的所有data
var request = store.getAll(IDBKeyRange.upperBound(+new Date()));
/*
*更新成功
*/
request.onsuccess = function(event) {
console.log('indexedDB getAll:', event.target.result);
};
/*
*更新失败
*/
request.onerror = function(event) {
console.log('indexedDB getAll:', event);
};
indexedDB 并非无底洞,可以无限存储。要考虑做定期删除等功能
1. 首先我们创建数据的时候就以时间戳+失效时间来约定id规则
2. 再通过上面基础操作的getAll
方法,获取指定条件的data
,再遍历data
,调用删除数据API
var store = db.transaction(['group'], 'readwrite').objectStore('group');
var request = store.getAll(IDBKeyRange.upperBound(+new Date()));
/*
*更新成功
*/
request.onsuccess = function(event) {
console.log('indexedDB getAll:', event);
console.log('indexedDB getAll:', event.target.result);
const data = event.target.result;
data.forEach(item => {
console.log('删除数据', item);
const deletRequest = store.delete(item.id);
/*
*删除成功
*/
deletRequest.onsuccess = function(event) {
console.log('数据删除成功', event);
};
/*
*删除失败
*/
deletRequest.onerror = function(event) {
console.log('数据删除失败', event);
};
});
};
/*
*更新失败
*/
request.onerror = function(event) {};
3. 我们把上述方法包装下每次打开页面,清空下失效数据。就可以实现一个定期删除失效数据的方法啦
const TestData = [
{
event: 'NE-TEST1',
level: 'warning',
errorCode: 200,
url: 'http://www.example.com',
time: '2017/11/8 下午4:53:039',
isUploaded: false
},
{
event: 'NE-TEST2',
msg: '测试2',
level: 'error',
errorCode: 1000,
url: 'http://www.example.com',
time: '2017/11/8 下午4:53:042',
isUploaded: false
},
{
event: 'NE-TEST3',
msg: '测试3',
level: 'info',
errorCode: 3000,
url: 'http://www.example.com',
time: '2017/11/8 下午4:53:043',
isUploaded: false
},
{
event: 'NE-TEST4',
mgs: '测试4',
level: 'info',
url: 'http://www.example.com',
time: '2017/11/8 下午4:53:0423',
isUploaded: false
}
]
/**
* 添加数据
* @param {array} docs 要添加数据
* @param {string} objName 仓库名称
*/
function addData (docs, objName) {
if (!(docs && docs.length)) {
throw new Error('docs must be a array!')
}
return openIndexedDB().then(db => {
const tx = db.transaction([objName], 'readwrite')
tx.oncomplete = e => {
console.log('tx:addData onsuccess', e)
return Promise.resolve(docs)
}
tx.onerror = e => {
e.stopPropagation()
console.error('tx:addData onerror', e.target.error)
return Promise.reject(e.target.error)
}
tx.onabort = e => {
console.warn('tx:addData abort', e.target)
return Promise.reject(e.target.error)
}
const obj = tx.objectStore(objName)
docs.forEach(doc => {
const req = obj.add(doc)
/**
* NOTE:
* request
* 两个事件:
* 1. success
* 2. error
*/
// req.onsuccess = e => console.log('obj:addData onsuccess', e.target)
req.onerror = e => {
console.error('obj:addData onerror', e.target.error)
}
})
})
}
addData(TestData, OB_NAMES.UseKeyGenerator)
.then(() => addData(TestData, OB_NAMES.UseKeyPath))
localforage
推荐 ⭐️⭐️⭐️⭐️⭐️ (我们当前业务就用的这个~)和localsotrage
使用保持一致,更适合前端使用
还在用localStorage?快来试试localForage吧! - 掘金
IndexedDBWrapper
推荐 ⭐️⭐️⭐️在做一些前端electron
应用,webApp
,我们可以定义一个log
日志库,来收集用户日志,遇到问题时,可以让用户,打包上传到日志库,排查跟进解决用户反馈问题。
定义日志上报结构
// 定义log基本结构
const LogItem = {
level: 'log' | 'info' | 'error' ...,
tag: 'request' | 'system' | ‘video’ | 'audio' | 'domClick' ... ,
msg: ..., // any
date: +new Date(),
...
}
导出所有数据,并上传
此处就可以用上面的 getAll
方法,获取该表所有数据,生成json
打包上传到自己公司的日志库。
request
层封装,对不长更新接口缓存封装request方法,缓存请求接口,物理缓存数据~让页面接口数据加载飞起来
文件切片后先存储到indexedDB
库,动态更新上传状态,异常状况可取出再继续定位到未上传的切片继续上传。