WebAPI - IndexedDB详解

IndexedDB 是一个运行在浏览器中的事务型数据库系统,用于在客户端存储大量结构化数据

核心特点:

1.键值对存储:基于 JavaScript 对象存储数据
2.异步操作:所有操作都是异步的,不会阻塞主线程
3.支持事务:保证数据操作的原子性和一致性
4.大容量存储:通常可以存储数百MB甚至GB级别的数据
5.索引查询:支持创建索引进行高效查询
6.同源策略:每个源都有独立的数据库空间

使用方法:

  1. 打开数据库:indexedDB.open()
const request = indexedDB.open(name, version);
name (字符串): 数据库的名称。如果指定的数据库不存在,则会创建该数据库。
version (可选, 整数): 数据库的版本。当打开一个版本号比当前已存在的数据库版本号高的数据库时,会触发 onupgradeneeded 事件。如果未指定,则默认打开当前版本的数据库(如果数据库已存在)或版本1的数据库(如果数据库不存在)。
  1. 删除数据库:indexedDB.deleteDatabase()
const request = indexedDB.deleteDatabase(name);
name (字符串): 要删除的数据库的名称。
  1. 创建对象存储:IDBDatabase.createObjectStore()
// 该方法在 onupgradeneeded 事件中调用。
const objectStore = db.createObjectStore(name, options);
name (字符串): 对象存储的名称。
options (可选, 对象): 配置对象,包含以下属性:
keyPath (可选, 字符串或数组): 指定对象存储中用作键的属性路径。如果指定,则存储的每个对象必须在该路径上具有唯一值。如果省略,则必须使用显式键(如 add 方法提供键)。
autoIncrement (可选, 布尔值): 如果为 true,则对象存储使用自动递增的键生成器。默认为 false。
  1. 创建索引:IDBObjectStore.createIndex()
const index = objectStore.createIndex(name, keyPath, options);
name (字符串): 索引的名称。
keyPath (字符串或数组): 指定索引所基于的对象属性的路径。如果是数组,则使用复合键。
options (可选, 对象): 配置对象,包含以下属性:
unique (可选, 布尔值): 如果为 true,则索引不允许重复值。默认为 false。
multiEntry (可选, 布尔值): 如果为 true,则当 keyPath 解析为数组时,索引将为数组中的每个元素创建一个条目。如果为 false,则它将整个数组作为单个条目。默认为 false。
  1. 事务:IDBDatabase.transaction()
const transaction = db.transaction(storeNames, mode);
storeNames (字符串或字符串数组): 事务要涉及的对象存储的名称。可以是一个字符串(单个存储)或字符串数组(多个存储)。
mode (可选, 字符串): 事务模式,可以是以下之一:
	"readonly": 只读,默认值。
	"readwrite": 读写。
	"versionchange": 用于版本变更(如创建/删除对象存储和索引)。通常只在 onupgradeneeded 事件中使用。
  1. 添加数据:IDBObjectStore.add()
const request = objectStore.add(value, key);
value: 要存储的值。
key (可选): 用于标识记录的键。如果对象存储使用 keyPath,则不应提供此参数,否则必须提供。
  1. 更新数据:IDBObjectStore.put()
const request = objectStore.put(value, key);
value: 要存储的值。
key (可选): 用于标识记录的键。如果对象存储使用 keyPath,则可以从值中提取键,否则必须提供。
  1. 删除数据:IDBObjectStore.delete()
const request = objectStore.delete(key);
key: 要删除的记录的键。
  1. 清空对象存储:IDBObjectStore.clear()
const request = objectStore.clear();
无参数。
  1. 获取数据:IDBObjectStore.get()
const request = objectStore.get(key);
key: 要获取的记录的键。
  1. 获取所有数据:IDBObjectStore.getAll()
const request = objectStore.getAll(query, count);
query (可选): 键范围或键,用于限制返回的记录。如果未提供,则返回所有记录。
count (可选): 指定要返回的记录数量。如果未提供,则返回所有匹配的记录。
  1. 获取键:IDBObjectStore.getKey()
const request = objectStore.getKey(key);
key: 要获取的记录的键,返回该键对应的主键(如果存在)。
  1. 获取所有键:IDBObjectStore.getAllKeys()
const request = objectStore.getAllKeys(query, count);
query (可选): 键范围或键,用于限制返回的键。如果未提供,则返回所有键。
count (可选): 指定要返回的键的数量。如果未提供,则返回所有匹配的键。
  1. 计数:IDBObjectStore.count()
const request = objectStore.count(key);
key (可选): 键范围或键,用于限制计数的记录。如果未提供,则计数所有记录。
  1. 打开游标:IDBObjectStore.openCursor()
const request = objectStore.openCursor(range, direction);
range (可选): 键范围,用于限制游标遍历的记录。如果未提供,则遍历所有记录。
direction (可选, 字符串): 游标遍历的方向,可以是以下之一:
	"next": 默认值,从第一个记录开始向前遍历。
	"nextunique": 向前遍历,但跳过重复的索引键(仅用于索引游标)。
	"prev": 从最后一个记录开始向后遍历。
	"prevunique": 向后遍历,但跳过重复的索引键(仅用于索引游标)。
  1. 打开键游标:IDBObjectStore.openKeyCursor()
const request = objectStore.openKeyCursor(range, direction);
参数同 openCursor,但游标只返回键而不返回值。
  1. 索引方法

索引对象(IDBIndex)具有与对象存储类似的方法,如 get, getKey, getAll, getAllKeys, count,
openCursor, openKeyCursor。它们的参数与对象存储的对应方法相同,但操作的是索引而不是整个对象存储


18. 游标方法
游标(IDBCursor)有以下方法:

continue(key): 将游标移动到下一个位置(或指定的键)。
key (可选): 如果提供,游标将移动到指定的键。
continuePrimaryKey(key, primaryKey): 仅用于索引游标,移动到指定的索引键和主键。
key: 索引键。
primaryKey: 主键。
advance(count): 将游标向前移动指定数量的记录。
count: 要跳过的记录数。
update(value): 更新当前游标位置的记录。
value: 新的值。
delete(): 删除当前游标位置的记录。

  1. 键范围
    键范围(IDBKeyRange)的静态方法:

IDBKeyRange.only(value): 创建一个只包含指定键的范围。 value: 键值。
IDBKeyRange.lowerBound(lower, open): 创建一个从指定键开始到无穷大的范围。
lower: 下界键值。
open (可选, 布尔值): 如果为 true,则范围不包括下界。默认为 false。
IDBKeyRange.upperBound(upper, open): 创建一个从无穷小到指定键的范围。
upper: 上界键值。
open (可选, 布尔值): 如果为 true,则范围不包括上界。默认为 false。
IDBKeyRange.bound(lower,upper, lowerOpen, upperOpen): 创建一个介于两个键之间的范围。
lower: 下界键值。
upper:上界键值。
lowerOpen (可选, 布尔值): 如果为 true,则范围不包括下界。默认为 false。
upperOpen (可选,布尔值): 如果为 true,则范围不包括上界。默认为 false。

封装方法与案例:

class IndexedDBManager {
	  constructor(dbName, version = 1) {
	    this.dbName = dbName;
	    this.version = version;
	    this.db = null;
	  }
	  
	  // 初始化数据库
	  async init() {
	    this.db = await this.openDatabase();
	    return this;
	  }
	  
	  // 打开数据库
	  openDatabase() {
	    return new Promise((resolve, reject) => {
	      const request = indexedDB.open(this.dbName, this.version);
	      
	      request.onerror = () => reject(request.error);
	      request.onsuccess = () => resolve(request.result);
	      
	      request.onupgradeneeded = (event) => {
	        const db = event.target.result;
	        this.setupDatabase(db, event.oldVersion);
	      };
	    });
	  }
	  
	  // 设置数据库结构
	  setupDatabase(db, oldVersion) {
	    // 创建用户表
	    if (!db.objectStoreNames.contains('users')) {
	      const store = db.createObjectStore('users', {
	        keyPath: 'id',
	        autoIncrement: true
	      });
	      
	      store.createIndex('email', 'email', { unique: true });
	      store.createIndex('name', 'name', { unique: false });
	      store.createIndex('age', 'age', { unique: false });
	      store.createIndex('createdAt', 'createdAt', { unique: false });
	    }
	    
	    // 版本2:添加产品表
	    if (oldVersion < 2 && !db.objectStoreNames.contains('products')) {
	      const store = db.createObjectStore('products', {
	        keyPath: 'sku'
	      });
	      
	      store.createIndex('category', 'category', { unique: false });
	      store.createIndex('price', 'price', { unique: false });
	    }
	  }
	  
	  // 通用CRUD操作
	  async add(storeName, data) {
	    const store = this.getStore(storeName, 'readwrite');
	    return await this.promisifyRequest(store.add(data));
	  }
	  
	  async get(storeName, key) {
	    const store = this.getStore(storeName, 'readonly');
	    return await this.promisifyRequest(store.get(key));
	  }
	  
	  async put(storeName, data) {
	    const store = this.getStore(storeName, 'readwrite');
	    return await this.promisifyRequest(store.put(data));
	  }
	  
	  async delete(storeName, key) {
	    const store = this.getStore(storeName, 'readwrite');
	    return await this.promisifyRequest(store.delete(key));
	  }
	  
	  async getAll(storeName) {
	    const store = this.getStore(storeName, 'readonly');
	    return await this.promisifyRequest(store.getAll());
	  }
	  
	  async getByIndex(storeName, indexName, value) {
	    const store = this.getStore(storeName, 'readonly');
	    const index = store.index(indexName);
	    return await this.promisifyRequest(index.get(value));
	  }
	  
	  // 获取存储空间
	  getStore(storeName, mode) {
	    const transaction = this.db.transaction([storeName], mode);
	    return transaction.objectStore(storeName);
	  }
	  
	  // 将请求转换为Promise
	  promisifyRequest(request) {
	    return new Promise((resolve, reject) => {
	      request.onsuccess = () => resolve(request.result);
	      request.onerror = () => reject(request.error);
	    });
	  }
	  
	  // 关闭数据库
	  close() {
	    if (this.db) {
	      this.db.close();
	      this.db = null;
	    }
	  }
	}
	
	// 使用示例
	async function example() {
	  const dbManager = new IndexedDBManager('MyAppDB', 2);
	  await dbManager.init();
	  
	  // 添加用户
	  const userId = await dbManager.add('users', {
	    name: '李白',
	    email: 'libai@example.com',
	    age: 29,
	    createdAt: new Date()
	  });
	  
	  // 获取用户
	  const user = await dbManager.get('users', userId);
	  
	  // 通过邮箱查找用户
	  const userByEmail = await dbManager.getByIndex('users', 'email', 'zhang@example.com');
	  
	  // 更新用户
	  await dbManager.put('users', {
	    id: userId,
	    ...user,
	    age: 26,
	    updatedAt: new Date()
	  });
	  
	  // 删除用户
	  await dbManager.delete('users', userId);
	  
	  dbManager.close();
	}
	
	example().catch(console.error);
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值