在使用的过程中我们通常使用cache缓存html、css、js等文件信息,但是一些特殊的数据需要我们借助数据库的支持,这里推荐使用IndexedDB。
IndexedDB是一个大型的noSQL存储系统。 它使您可以在用户的浏览器中存储任何内容。 除了通常的搜索,获取和存储操作之外,IndexedDB还支持事务。
检查浏览器是否支持IndexedDB
if (!('indexedDB' in window)) {
console.log('This browser doesn\'t support IndexedDB');
return;
}
创建数据库
使用IndexedDB我们可以创建多个数据库,但是原则上我们每个应用程序只需要建立一个数据库。
name:数据库名称,version:版本号,upgradeCallback:回调方法
idb.open(name, version, upgradeCallback)
IndexedDB之对象存储
主要通过创建数据库是回调返回的实例调用createObjectStore方法来创建对象存储。
if (!('indexedDB' in window)) {
console.log('This browser doesn\'t support IndexedDB');
return;
}
var dbPromise = idb.open('test-db2', 1, function(upgradeDb) {
console.log('making a new object store');
if (!upgradeDb.objectStoreNames.contains('firstOS')) {
upgradeDb.createObjectStore('firstOS');
}
});
- 定义主键并设置自增
使用keyPath定义主键,使用autoIncrement设置主键自增。
var dbPromise = idb.open('test-db3', 1, function(upgradeDb) {
if (!upgradeDb.objectStoreNames.contains('logs')) {
upgradeDb.createObjectStore('logs', {keyPath: 'id', autoIncrement: true});
}
});
- 定义索引
创建索引,在对象存储实例上调用createIndex方法:
objectStore.createIndex('indexName','property',options);
这个方法创建并返回一个索引对象。 createIndex将新索引的名称作为第一个参数,第二个参数指向要索引的数据的属性。最后一个参数可以让你定义两个选项来决定索引如何工作:unique和multiEntry。如果unique设置为true,则索引不允许单个键的重复值。当索引属性是一个数组时,multiEntry决定了createIndex的行为。如果设置为true,则createIndex将在每个数组元素的索引中添加一个条目。否则,它会添加一个包含该数组的条目。
var dbPromise = idb.open('test-db4', 1, function(upgradeDb) {
if (!upgradeDb.objectStoreNames.contains('people')) {
var peopleOS = upgradeDb.createObjectStore('people', {keyPath: 'email'});
peopleOS.createIndex('gender', 'gender', {unique: false});
peopleOS.createIndex('ssn', 'ssn', {unique: true});
}
});
注意:每次将数据写入参考对象库时,索引都会更新。 索引越多意味着IndexedDB的工作量越大。
IndexedDB之操作数据
IndexedDB中的所有数据操作都是在一个事务中执行的。 每个操作都有这样的形式:
1.获取数据库对象
2.在数据库上打开事务
3.在事务上打开对象存储
4.在对象存储上执行操作
写入数据
要创建数据,请在对象存储上调用add方法,并传入要添加的数据。 Add有一个可选的第二个参数,它可以让你在创建时为单个对象定义主键,但是只有当你没有在createObjectStore中指定关键路径时才能使用它。
someObjectStore.add(data, optionalKey);
dbPromise.then(function(db) {
var tx = db.transaction('store', 'readwrite');//开启读写事物
var store = tx.objectStore('store');
var item = {
name: 'sandwich',
price: 4.99,
description: 'A very tasty sandwich',
created: new Date().getTime()
};
store.add(item);
return tx.complete;
}).then(function() {
console.log('added item to the store os!');
});
读取数据
要读取数据,请调用对象存储上的get方法。 get方法使用要从存储中检索的对象的主键。
someObjectStore.get(primaryKey);
dbPromise.then(function(db) {
var tx = db.transaction('store', 'readonly');//开启只读事物
var store = tx.objectStore('store');
return store.get('sandwich');//如果没有该值则返回undefined
}).then(function(val) {
console.dir(val);
});
更新数据
要更新数据,请在对象存储上调用put方法。 put方法与add方法非常相似,可以用来代替add来在对象存储中创建数据。 像add一样,put一个数据和一个可选的主键:
someObjectStore.put(data, optionalKey);
dbPromise.then(function(db) {
var tx = db.transaction('store', 'readwrite');
var store = tx.objectStore('store');
var item = {
name: 'sandwich',
price: 99.99,
description: 'A very tasty, but quite expensive, sandwich',
created: new Date().getTime()
};
store.put(item);
return tx.complete;
}).then(function() {
console.log('item updated!');
});
删除数据
要删除数据,在存储对象上调用delete方法。
someObjectStore.delete(primaryKey);
dbPromise.then(function(db) {
var tx = db.transaction('store', 'readwrite');
var store = tx.objectStore('store');
store.delete(key);
return tx.complete;
}).then(function() {
console.log('Item deleted');
});
获取所有数据
我们还可以使用getAll方法或使用游标从对象存储或索引中检索所有数据(或子集)。
- 使用getAll方法
此方法返回与指定的键或键范围匹配的对象存储中的所有对象。
someObjectStore.getAll(optionalConstraint);
dbPromise.then(function(db) {
var tx = db.transaction('store', 'readonly');
var store = tx.objectStore('store');
return store.getAll();
}).then(function(items) {
console.log('Items by name:', items);
});
- 使用游标
另一种检索所有数据的方法是使用游标。 游标会逐个选择对象存储或索引中的每个对象,让您在选择数据时对其执行操作。
someObjectStore.openCursor(optionalKeyRange, optionalDirection);
dbPromise.then(function(db) {
var tx = db.transaction('store', 'readonly');
var store = tx.objectStore('store');
return store.openCursor();
}).then(function logItems(cursor) {
if (!cursor) {
return;
}
console.log('Cursored at:', cursor.key);
for (var field in cursor.value) {
console.log(cursor.value[field]);
}
return cursor.continue().then(logItems);
}).then(function() {
console.log('Done cursoring');
});
这个方法返回一个promise,用游标对象表示对象存储中的第一个对象,如果没有对象,则返回undefined。 要移动到对象存储中的下一个对象,我们调用cursor.continue。
我们首先获取数据库对象,创建一个事务,并打开一个对象存储。我们在对象存储上调用openCursor方法,并将游标对象传递给.then中的回调函数。这次我们将回调函数命名为“logItems”,所以我们可以从函数内部调用它并进行循环。 if(!cursor){return;}如果store.openCursor()返回的promise被解析为undefined,或者cursor.continue()返回的promise被解析为undefined(表示没有更多的对象)。
游标对象包含一个代表项目主键的键属性。它还包含一个代表数据的值属性。在logItems结束时,我们返回cursor.continue().then(logItems)。 cursor.continue方法返回一个promise,该promise解析为表示store中下一个store的游标对象,如果没有更多的对象,则返回undefined。这个结果被传递到.then中的回调函数中,我们选择把logItems作为这个函数,这样函数就会循环。因此,logItems继续调用自己,直到没有对象保留。
通过索引获取某个范围内数据
我们可以通过不同的方式获得所有的数据,但是如果我们只需要基于某个特定属性的数据子集呢? 这时我们可以使用索引。索引让我们通过主键以外的属性获取对象存储中的数据。 我们可以在任何属性上创建索引,在该属性上指定范围,并使用getAll方法或游标获取范围内的数据。
我们使用IDBKeyRange对象来定义范围。 这个对象有四个方法来定义范围的限制:upperBound,lowerBound,bound(这意味着两者),only。 upperBound和lowerBound方法指定范围的上限和下限。
IDBKeyRange.lowerBound(indexKey); IDBKeyRange.upperBound(indexKey); IDBKeyRange.bound(lowerIndexKey, upperIndexKey); IDBKeyRange.only(indexKey);
function searchItems(lower, upper) {
if (lower === '' && upper === '') {return;}
var range;
if (lower !== '' && upper !== '') {
range = IDBKeyRange.bound(lower, upper);
} else if (lower === '') {
range = IDBKeyRange.upperBound(upper);
} else {
range = IDBKeyRange.lowerBound(lower);
}
dbPromise.then(function(db) {
var tx = db.transaction(['store'], 'readonly');
var store = tx.objectStore('store');
var index = store.index('price');//打开索引
return index.openCursor(range);
}).then(function showRange(cursor) {
if (!cursor) {return;}
console.log('Cursored at:', cursor.key);
for (var field in cursor.value) {
console.log(cursor.value[field]);
}
return cursor.continue().then(showRange);
}).then(function() {
console.log('Done cursoring');
});
}
使用数据库版本号
var dbPromise = idb.open('test-db7', 3, function(upgradeDb) {
switch (upgradeDb.oldVersion) {
case 0:
upgradeDb.createObjectStore('store', {keyPath: 'name'});
case 1:
var storeOS = upgradeDb.transaction.objectStore('store');
storeOS.createIndex('price', 'price');
case 2:
var storeOS = upgradeDb.transaction.objectStore('store');
storeOS.createIndex('description', 'description');
}
});
如果浏览器中没有该数据库,那么oldVersion将为0,从case:0开始,依次执行0、1、2,创建主键为name的存储对象,然后为该对象创建一个price的索引和一个description的索引。
如果浏览器中存在该数据库,并且已经创建了一个名为price的索引,那么它的oldVersion为2,则0,1将被跳过,直接创建一个名为description的索引。
注意:这里的case没有break;
参考链接:
https://developers.google.com/web/ilt/pwa/working-with-indexeddb#getting_all_the_data