IndexedDB 是浏览器存储数据结构化数据的一个方案,用来代替已经废弃的Web SQL。另外,IndexedDB的几乎都是异步执行的。
- IndexedDB 五大重点:
- 数据库(database):类似于MySQL等数据库,但与这些传统的数据库的区别在于 IndexedDB使用的是
对象存储而不是表格存储; - 对象存储(objectStore):创立数据库库之后,就要创建对象存储来存储数据。对象存储类似一张表,只不过存的是多个对象;
- 事务(transaction):创建对象存储后,所有的操作都是通过事务完成的,比如对象存储的curd(增删改查);
- 索引(index):用来快速访问对象存储上的每一项数据;
- 游标:在对象存储中数据项中游走,可以用来获取其他项数据;
- 数据库(database):类似于MySQL等数据库,但与这些传统的数据库的区别在于 IndexedDB使用的是
1. 使用数据库
首先检查浏览器是否支持 IndexedDB,如果支持则使用 indexedDB;
iOS 8对IndexedDB支持不友好,可以按不支持处理
// 1. 检查浏览器是否支持 IndexedDB
function idbOK() {
return "indexedDB" in window && !/iPad|iPhone|iPod/.test(navigator.platform);
}
if (idbOK()) {
let db;
// 2. 打开数据库:echo_db为数据库名,1为版本号
let openRequest = window.indexedDB.open('echo', 1);
// 3. 第一次使用数据库,upgradeneeded 事件会被触发,另外打开新版本也会
openRequest.onupgradeneeded = function(event) {
const thisDB = event.target.result; // event.target.result为数据库实例
console.log('openRequest() onupgradeneeded', event.target.result);
}
// 4. 数据库打开成功
openRequest.onsuccess = (event) => {
db = event.target.result; // event.target.result为数据库实例
console.log('openRequest() onsuccess', event.target.result);
}
// 5. 数据库打开失败
openRequest.onerror = (event) => {
console.log('openRequest() onerror', event.target.errorCode);
}
}
2. 使用对象存储
对象存储只能在 upgradeneeded 事件处理期间创建。因此第一次使用数据库,或者更新版本后才能创建新的对象存储;
// ...
// 第一次使用数据库,upgradeneeded 事件会被触发,另外打开新版本也会
openRequest.onupgradeneeded = function(event) {
const thisDB = event.target.result; // event.target.result为数据库实例
console.log('openRequest() onupgradeneeded', event.target.result);
if(!thisDB.objectStoreNames.contains("users")) {
// const store= thisDB.createObjectStore("users", { keyPath: "name" }); // 设置主键为‘name’
const store= thisDB.createObjectStore("users", { keyPath: "id", autoIncrement:true }); // 主键名为id,且使用一个自增值自动为主键赋值
store.createIndex("name", "name", { unique: true }); // 设置索引
}
}
// ...
- 索引:用来查询对象存储中的数据项,加快检索的速度
- store.createIndex(indexName, indexPropotyName(索引的数据项属性), {unique: true, ...}):创建新索引
- store.indexNames:获取对象存储上所有的索引
- store.index(indexName): 使用索引
- store.deleteIndex(indexName): 删除索引
- 主键:是唯一性的索引,避免对象存储中有相同的记录
- 创建对象存储时,必须创建
3. 操作数据
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script type="text/javascript" src = "http://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
</head>
<body>
<h2>新增</h2>
<div>
名字:
<input type="text" id="name" placeholder="name">
<br/>
邮箱:
<input type="email" id="email" placeholder="Email">
<br/>
<button id="addUser">Add Users</button>
</div>
<h2>删除</h2>
<div>
主键:
<input type="number" id="del_id" placeholder="name">
<button id="deleteUser">Delete Users</button>
</div>
<h2>修改</h2>
<div>
主键:
<input type="number" id="edit_id" placeholder="name">
<br/>
名字:
<input type="text" id="edit_name" placeholder="name">
<br/>
邮箱:
<input type="email" id="edit_email" placeholder="Email">
<br/>
<button id="modifyUser">Modify Users</button>
</div>
<div>
<h2>查询</h2>
<div>
主键:
<input type="number" id="query_id" placeholder="name">
<button id="queryUser">Query Users</button>
</div>
</div>
<div>
<h2>游标学习</h2>
<div>
主键:
<input type="number" id="cursor_id" placeholder="name">
<button id="cursorUser">Cursor</button>
</div>
</div>
<script>
function idbOK() {
return "indexedDB" in window && !/iPad|iPhone|iPod/.test(navigator.platform);
}
if (idbOK()) {
let db;
let openRequest = window.indexedDB.open('echo', 1); // 打开数据库:echo_db为数据库名,1为版本号
// 第一次使用数据库,upgradeneeded 事件会被触发,另外打开新版本也会
openRequest.onupgradeneeded = function(event) {
const thisDB = event.target.result; // event.target.result为数据库实例
console.log('openRequest() onupgradeneeded', event.target.result);
if(!thisDB.objectStoreNames.contains("users")) {
// const store= thisDB.createObjectStore("users", { keyPath: "name" }); // 设置主键为‘name’
const store= thisDB.createObjectStore("users", { keyPath: "id", autoIncrement:true }); // 主键名为id,且使用一个自增值自动为主键赋值
store.createIndex("name", "name", { unique: true }); // 设置索引
}
}
// 数据库打开成功
openRequest.onsuccess = (event) => {
db = event.target.result; // event.target.result为数据库实例
console.log('openRequest() onsuccess', event.target.result);
// 开始监听按钮点击
$("#addUser").on("click", addUser);
$("#deleteUser").on("click", deleteUser);
$("#modifyUser").on("click", modifyUser);
$("#queryUser").on("click", queryUser);
$("#cursorUser").on("click", cursorUser);
}
// 数据库打开失败
openRequest.onerror = (event) => {
console.log('openRequest() onerror', event.target.errorCode);
}
function addUser(e) {
const name = $("#name").val();
const email = $("#email").val();
//事务处理
const transaction = db.transaction(["users"],"readwrite"); // 1. transaction以下有展开描述
// 获取名为users的对象存储
const store = transaction.objectStore("users"); // 2. store以下有展开描述
//定义person
const person = {
name,
email,
created:new Date().getTime()
}
//添加数据
const request = store.add(person);
// 错误处理器
request.onerror = function(e) {
console.log("addUser(): onerror",e.target.error.name);
}
request.onsuccess = function(e) {
console.log("addUser(): onsuccess");
}
}
function deleteUser(e) {
const key = +$("#del_id ").val();
//事务处理
const transaction = db.transaction(["users"],"readwrite");
const store = transaction.objectStore("users");
var request = store.delete(key);
request.onerror = function(e) {
console.log("deleteUser(): onerror",e.target.error.name);
}
request.onsuccess = function(e) {
console.log("deleteUser(): onsuccess");
}
}
function modifyUser() {
const id = +$("#edit_id").val();
const name = $("#edit_name").val();
const email = $("#edit_email").val();
//事务处理
const transaction = db.transaction(["users"],"readwrite");
const store = transaction.objectStore("users"); // 获取名为users的对象存储
//定义person
const person = {
id,
name,
email,
created:new Date().getTime()
}
//添加数据
const request = store.put(person);
// 错误处理器
request.onerror = function(e) {
console.log("modifyUser(): onerror",e.target.error.name);
}
request.onsuccess = function(e) {
console.log("modifyUser(): onsuccess,", e.target.result);
}
}
function queryUser() {
const key = +$("#query_id ").val();
//事务处理
const transaction = db.transaction(["users"],"readwrite");
const store = transaction.objectStore("users");
var request = store.get(key);
request.onerror = function(e) {
console.log("queryUser(): onerror",e.target.error.name);
}
request.onsuccess = function(e) {
console.log("queryUser(): onsuccess,", e.target.result);
}
}
function cursorUser() {
const key = +$("#query_id ").val();
//事务处理
const transaction = db.transaction("users");
const store = transaction.objectStore("users");
const request = store.openCursor();
request.onerror = function(e) {
console.log("queryUser(): onerror",e.target.error.name);
}
request.onsuccess = function(e) {
console.log("queryUser(): onsuccess,", e.target.result);
}
}
}
</script>
</body>
</html>
transaction- 第一个属性:
- 无: 获取所有对象存储;
- objectStoreName: 获取单个对象存储;
- [objectStoreName, ...]: 多个对象存储
- 第二个属性:
- 无:默认值 readonly;
- readwrite:读写;
- versionchange:版本改变
- 第一个属性:
store- store.add(data): 添加数据
- store.get(index): 根据主键获取数据
- store.delete(index): 根据主键删除数据
- store.put(data):修改数据(数据中需包含主键)
4. 游标
使用事务可以通过一个已知键取得一条记录。如果想取得多条数据,则需要在事务中创建一个游标。
你可以将游标想象成一只快乐的小海狸,它跑进对象存储,一次取回一条数据。它每取得一条数据就带回来给你,而你又要求它去取下一条。游标可以双向移动(海狸也可以),也可以被限制在一定的数据“范围”内。
function cursorUser() {
const key = +$("#query_id ").val();
//事务处理
const transaction = db.transaction("users");
const store = transaction.objectStore("users");
const request = store.openCursor(); // 1. openCursor以下有展开描述
request.onerror = function(e) {
console.log("queryUser(): onerror",e.target.error.name);
}
request.onsuccess = function(e) {
const cursor = event.target.result;
console.log("queryUser(): onsuccess,", cursor); // 2. cursor以下有展开描述
}
}
openCursor- 参数:可选
- 第一个参数
IDBKeyRange.only(key):直接访问主键为key的记录IDBKeyRange.lowerBound(key):游标从"key"这个键开始,直到最后(包含指定的键)IDBKeyRange.lowerBound(key, true):游标从"key"的下一条记录开始,直到最后(不包含指定的键)IDBKeyRange.upperBound(key):游标从头开始,到"key"记录为止(包含指定的键)IDBKeyRange.upperBound(key, true):游标从头开始,到"key"的前一条记录为止(不包含指定的键)IDBKeyRange.bound(start, end, isUninvoled?, isUninvoled?): 下限的键、上限的键、是否跳过下限、是否跳过上限
- 第二个参数
prev: 向最后一条记录前进prevunique: 向最后一条记录前进, 且跳过重复记录next: 向第一条记录前进nextunique: 向第一条记录前进, 且跳过重复记录
- 第一个参数
- 参数:可选
cursor- 属性:
direction:字符串常量("next"、"nextunique"、"prev"、"prevunique"),表示游标的前进方向以及是否应该遍历所有重复的值;key:对象的键。value:实际的对象。primaryKey:游标使用的键。可能是主键或索引键。
- 方法:
- continue(key?): 参数 key 是可选的。如果没有指定 key,游标就移动到下一条记录;如果指定了,则游标移动到指定的键
- advance(count):游标向前移动指定的 count 条记录
- 属性: