由于之前项目需求,循环播放广告需要对视频、图片做本地存储,以节省移动端流量及播放视频的稳定性,翻阅资料后,找到了IndexedDB
WebSQL DB与IndexedDB
打开chrome,按F12进入调试,切换到Application
分页
可以看到,前端数据库有
WebSQL DB
和IndexedDB
两种,而Cookies、SessionStorage、LocalStorage
都只能存储少量数据,通常只存储一些字符串,在这里不予考虑。
WebSQL DB
是一个关系型数据库,使用sql查询语句,可以存储大型数据,但是不支持IE及火狐,其兼容性可以在https://caniuse.com网站中查看,并且W3C规范已经停止对WebSQL DB
的维护,将来也可能不再支持。
The Web SQL Database specification is no longer being maintained and support may be dropped in future versions.
所以,项目中并不再推荐使用WebSQL DB
。
而IndexedDb
兼容性较之要更好,并且目前也升级到了2.0,W3C也在大力推广其规范,综上考虑项目中就顺理成章的使用了IndexedDb
。
IndexedDb
则是一个非关系型数据库,不支持sql查询,更像是NoSql数据库。虽然其已经升级了2.0,但是兼容性比1.0要差不少,新增功能也只是对api的补全,所以目前只使用1.0版本记录。
IndexedDB
具有以下特点。
键值对储存。
IndexedDB
内部采用对象仓库(object store
)存放数据。所有类型的数据都可以直接存入,包括JavaScript
对象。对象仓库中,数据以"键值对"的形式保存,每一个数据记录都有对应的主键,主键是独一无二的,不能有重复,否则会抛出一个错误。异步。
IndexedDB
操作时不会锁死浏览器,用户依然可以进行其他操作,这与LocalStorage
形成对比,后者的操作是同步的。异步设计是为了防止大量数据的读写,拖慢网页的表现。支持事务。
IndexedDB
支持事务(transaction
),这意味着一系列操作步骤之中,只要有一步失败,整个事务就都取消,数据库回滚到事务发生之前的状态,不存在只改写一部分数据的情况。同源限制。
IndexedDB
受到同源限制,每一个数据库对应创建它的域名。网页只能访问自身域名下的数据库,而不能访问跨域的数据库。储存空间大。
IndexedDB
的储存空间比LocalStorage
大得多,一般来说不少于 250MB,甚至没有上限。支持二进制储存。
IndexedDB
不仅可以储存字符串,还可以储存二进制数据(ArrayBuffer
对象和Blob
对象)。
实例
1、创建数据库
this.request = window.indexedDB.open(databaseName, 1);
this.request.onerror = this.__dbOpenErr;
this.request.onsuccess = this.__dbOpenSuccess;
this.request.onupgradeneeded = this.__dbUpgradeneeded;
this.openCallback = callback;
使用window.indexedDB.open
打开数据库,第一个参数是字符串,表示数据库的名字。如果指定的数据库不存在,就会新建数据库。第二个参数是整数,表示数据库的版本。如果省略,打开已有数据库时,默认为当前版本;新建数据库时,默认为1
。
其返回值,是一个IDBRequest
对象,通过onerror , onsuccess , onupgradeneeded
三个回调方法处理操作结果。打开数据库成功后,其db对象也在IDBRequest
中获取
__dbOpenErr = (event) => {
console.log('数据库打开报错');
if(this.openCallback){
this.openCallback(false);
}
};
__dbOpenSuccess = (event) => {
this.db = this.request.result;
console.log("数据库打开成功");
if(this.openCallback){
this.openCallback(true);
}
};
成功和失败时,调用callback
通知上层。
const databaseName = "IndexedDB";
const storeNames = "Media";
// 数据库升级事件,第一次创建时会走这里
__dbUpgradeneeded = (event) => {
this.db = event.target.result;
let objectStore;
if (!this.db.objectStoreNames.contains(storeNames)) {
objectStore = this.db.createObjectStore(storeNames, { keyPath: 'keyPath' });
// objectStore.createIndex('blob', 'blob', { unique: false });
objectStore.createIndex('type', 'type', {unique: false});
objectStore.createIndex('url', 'url', {unique: false});
}
// if(this.openCallback){
// this.openCallback(true);
// }
};
onupgradeneeded
是数据库升级事件,当第一次打开数据库,由于数据库不存在也会走这里,所以需要在这里做数据库初始化,创建一个“表”(ObjectStore
)。上述代码,先判断了Media
是否存在,不存在就创建了一个叫Media
的“表”,主键名是keyPath
。如果不想指定主键,可以让IndexedDB
自动生成主键。
objectStore = this.db.createObjectStore(storeNames, { autoIncrement: true });
下一步是使用IDBObject.createIndex
创建索引,三个参数分别为索引名称、索引所在的属性、配置对象(说明该属性是否 不能重复,为true
不可重复,false
则可重复)。这里创建了type
和url
两个可重复索引。
2、新增数据
insert(data, callback) {
let request = this.db.transaction([storeNames], 'readwrite')
.objectStore(storeNames)
.add(data);
request.onsuccess = (event) => {
console.log('数据写入成功');
if(callback){
callback(true);
}
};
request.onerror = function (event) {
console.log('数据写入失败');
console.log(event);
if(callback){
callback(false);
}
}
}
写入数据需要新建一个事务。新建时必须指定表格名称和操作模式(readonly
或readwrite
)。新建事务以后,通过IDBTransaction.objectStore(name)
方法,拿到IDBObjectStore
对象,再通过表格对象的add()方法,向表格写入一条记录。
3、读取数据
// 主键搜索
searchByKeyPath(keyPath, callback){
let transaction = this.db.transaction([storeNames], 'readonly');
let store = transaction.objectStore(storeNames);
let request = store.get(keyPath);
request.onsuccess = (e) => {
let result = e.target.result;
if(callback){
callback(result);
}
};
request.onerror = (e) => {
if(callback){
callback(null);
}
};
}
使用objectStore
的get
方法读取数据,参数是主键值。如果你没指定主键,那就只能使用索引读取了。
4、索引
// 索引搜索
searchByUrl(url, callback) {
let transaction = this.db.transaction([storeNames], 'readonly');
let store = transaction.objectStore(storeNames);
let index = store.index('url');
// 安卓5.1不支持getAll方法
let request = index.get(url);
request.onsuccess = (e) => {
let result = e.target.result;
if(callback){
callback(result);
}
};
request.onerror = (e) => {
if(callback){
callback(null);
}
};
}
使用之前创建的url
索引进行查询数据,objectStore
对象的index
方法返回IDBIndex
对象,参数是索引名,再使用IDBIndex
的get
方法传入要查询的值,就可以从url
中查找到相应值的记录。注意:如果查找到多条记录,并不会返回一个数组,而是通过多次回调onsuccess
方法告知上层。
End~
写的时候,发现有大量的callback
,其实还是应该使用promise
重写,碍于最近没太多时间重写与测试,就没动源码。本日记发布也晚了两天,最近在跟hr扯皮、改写简历及准备面试,所以无暇顾及。