前端数据库indexedDB入门

Posted YOLO浪漫收藏家

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了前端数据库indexedDB入门相关的知识,希望对你有一定的参考价值。

什么是indexDB

        学习文档:

        indexDB是html5的新概念,indexedDB是一个用于在浏览器中存储较大数据结构的Web API,并且提供了索引功能以实现高性能查找。不同于其他基于SQL的关系型数据库,indexedDB是一个事务型的数据库系统,会将数据集作为个体对象存储,数据形式使用的是JSON,而不是列数固定的表格来存储数据的。

        indexDB比本地存储很强大,而且存储大小是250m以上(受计算机硬件和浏览器厂商的限制)。

  • indexDB优点是:存储容量大;支持异步操作;具有事务特点;
  • indexDB缺点是:不支持DO操作;不能跨域。

        indexDB中的对象:

  • 数据库:IDBDatabase 对象
  • 对象仓库:IDBObjectStore 对象
  • 索引: IDBIndex 对象
  • 事务: IDBTransaction 对象
  • 操作请求:IDBRequest 对象
  • 指针(游标): IDBCursor 对象
  • 主键集合:IDBKeyRange 对象

        基本语法:

语法

作用

window.indexedDB.open(数据库名,版本号)

打开数据库(如果数据库不存在则创建一个新的库)

.onerror

数据库操作过程中出错时触发

.onupgradeneeded

创建一个新的数据库或者修改数据库版本号时触发

.onsuccess

数据库成功完成所有操作时触发

.createObjectStore(对象仓库名称,keypath)

创建对象仓库

.createIndex(索引名称,keypath,objectParameters)

建立索引

.transaction(对象仓库名称) || .transaction(对象仓库名称,‘readwrite’)

创建一个事务 || 创建一个事务,并要求具有读写权限

.objectStore(对象仓库名称)

获取对象仓库

.get ( num ) || .getAll()

获取数据 || 获取全部数据

.add( data )

添加数据

.put( newdata )

修改数据

.delete ( keypath )

删除数据

        Demo:

        关于具体应用的demo可以看我的gitee上的Demo:

https://gitee.com/yinlingyun123/dragVuehttps://gitee.com/yinlingyun123/dragVue

indexDB基础操作:

        indeDB和一般的数据库一样,创建/打开数据库,创建/打开表,对数据的增删查改等都是其基本功能。

        1、判断浏览器是否支持IndexDB

function justifyIndexDEB()
    if("indexedDB" in window) 
        // 支持
        console.log(" 支持indexedDB...");
     else 
        // 不支持
        console.log("不支持indexedDB...");
    

        2、不同浏览器兼容

//数据库对象
window.indexedDB =window.indexedDB||window.webikitIndexedDB||window.mozIndexedDB||window.msIndexedDB;
//数据库事务
window.IDBTransaction = window.IDBTransaction||window.webikitIDBTransaction||window.mozIDBTransaction||window.msIDBTransaction;
//数据库查询条件
 window.IDBKeyRange = window.IDBKeyRange||window.webkitIDBKeyRange||window.mozIDBKeyRange||window.msIDBKeyRange;
//游标
window.IBDCursor =  window.IBDCursor ||window.webkitIBDCursor ||window.mozIBDCursor ||window.msIBDCursor ;

        3、创建/打开数据库

        如果存在就打开,不存在就创建一个indexDB数据库:

const req = window.indexedDB.open(databaseName, version)  // 数据库名称,版本号
let that = this;
req.onsuccess = function (event)  // 监听数据库创建成功事件
    that.indexDB = event.target.result // 数据库对象
    console.log('数据库打开成功')

req.onerror = function (error) 
    console.log('数据库打开报错')

req.onupgradeneeded = function (event) 
    // 数据库创建或升级的时候会触发
    console.log('数据库创建或升级')

        4、创建/打开/删除数据表

        在indexedDB中,是使用objectStore来存储数据呢。objectStore相当于一张表,但是objectStore并不想mysql中的表一样,具有一列一列的结构,它只有两列,一列是keypath(键值),另一列就是存储的数据了,存储的数据一般用javascript中的对象来表示。每一条数据都和一个键相关联。

        存储时可以使用每条记录中的某个指定字段作为键值(keyPath),也可以使用自动生成的递增数字作为键值(keyGenerator),也可以不指定。选择键的类型不同,objectStore可以存储的数据结构也有差异。

键类型

存储数据

不使用

任意值,但是没添加一条数据的时候需要指定键参数

keyPath

Javascript对象,对象必须有一属性作为键值

keyGenerator

任意值

都使用

Javascript对象,如果对象中有keyPath指定的属性则不生成新的键值,如果没有自动生成递增键值,填充keyPath指定属性

        创建表:

        通过数据库实例的createObjectStore(storeName,keyType)进行创建objectStore。这个方法有两个参数,一个是objectStore的名字,一个是创建表的键类型。

        创建表相当于修改了数据库的模式,所以这个操作应该放到onupgradeneeded中:

req.onupgradeneeded = function (event) 
    // 数据库创建或升级的时候会触发
    let db = event.target.result
    let storeName = 'product' // 表名
    if(!db.objectStoreNames.contains(storeName)) 
        // keyPath是主键键值,也可以不传然后设定autoIncrement:true自动创建键值
        db.createObjectStore(storeName,keyPath:'key');
    
    //db.deleteObjectStore(storeName);删除数据仓库方法,参数为数据仓库名称

        5、创建/删除索引

        索引可以用来搜索,主键是默认的索引。

        只能针对被设为索引的属性值进行检索,不能针对没有被设为索引的属性值进行检索。

        索引包含有联合索引,唯一索引,对数组字段建索引等等扩展功能,有需要的可以自己探索!

req.onupgradeneeded = function (event) 
    // 数据库创建或升级的时候会触发
    db = event.target.result
    let storeName = 'product' // 表名
    if (!db.objectStoreNames.contains(storeName))  // 判断表是否存在
        let objectStore = db.createObjectStore(storeName,  keyPath: 'key',autoIncrement: true )
        // 创建索引
        // indexName索引列名称
        // indexKey索引键值
        objectStore.createIndex('indexName', 'indexKey',  unique: false ) // 创建索引 可以让你搜索任意字段
        //objectStore.deleteIndex("indexName");删除索引
    

        给对象仓库(数据库表)创建索引,需要使用对象仓库的createIndex()函数,param1 索引名称,param2 配置索引的键值,param3 配置对象 配置该属性是否是唯一的。

        param3 配置对象可配置属性:

  • unique 唯一
  • multiEntry 对于有多个值的主键数组,每个值将在索引里面新建一个条目,否则主键数组对应一个条目

        6、添加数据

// this.indexDB创建数据库时候存下的数据库对象
// itemName是存储的key
// newValue是存储的value
const transaction = this.indexDB.transaction('product', "readwrite");
const store = transaction.objectStore('product');
const request = store.put( key: 'item', value: '<p>3333</p>' );
// const request = store.add( key: 'item', value: '<p>3333</p>' ); //add方法也可以
request.onsuccess=function(e) 
    console.info('添加数据成功')
;
request.onerror=function(e) 
    console.info('添加数据失败')
;

        7、获取数据

        get方法是获取定义的主键键值为输入数据的第一条数据,如果想获取匹配的所有数据就得用getAll方法进行获取。不过get、getAll方法需要查询整张表来获得结果,数据量大的时候效率很低。所以在日常使用中都是通过添加索引然后使用游标查询索引获取想要的数据。

// this.indexDB创建数据库时候存下的数据库对象
// itemName是存储的key
// newValue是存储的value
const transaction = this.indexDB.transaction('product', 'readwrite');
const store = transaction.objectStore('product');
const request=store.get('item');
request.onsuccess=function(e) 
    store.put( key: 'item', value: '<p>3333</p>' );
    console.info('获取数据成功')
;
request.onerror=function(e) 
    console.info('获取数据失败')
;

        8、更新数据

        更新数据操作其实就是添加和获取合二为一,在获取成功的回调函数里面进行数据的添加从而覆盖掉原来的数据:

// this.indexDB创建数据库时候存下的数据库对象
// itemName是存储的key
// newValue是存储的value
const transaction = this.indexDB.transaction('product', 'readwrite');
const store = transaction.objectStore('product');
const request=store.get('item');
request.onsuccess=function(e) 
    store.put( key: 'item', value: '<p>4444</p>' );
;

         9、删除数据

// this.indexDB创建数据库时候存下的数据库对象
// itemName是存储的key
// newValue是存储的value
const transaction = this.indexDB.transaction('product', 'readwrite');
const store = transaction.objectStore('product');
const request=store.delete('item');
request.onsuccess=function(e) 
    console.info('删除数据成功')
;
request.onerror=function(e) 
    console.info('删除数据失败')
;

        10、遍历数据

        使用指针对象IDBCursor遍历数据

objectStore.openCursor().onsuccess = function () 
   const cursor = e.target.result
   if (cursor) 
       console.log(cursor.key) // 当前遍历数据的主键
       console.log(cursor.value) // 当前遍历的数据
       cursor.continue() // 继续下一个
   

        可以通过配置IDBKeyRange来设定查询范围

// 游标查询范围内的多个:
// 除了bound 还有 only,lowerBound, upperBound 方法,还可以指明是否排除边界值
const range = IDBKeyRange.bound([min1, min2, min3], [max1, max2, max3]) 
// 传入的 prev 表示是降序遍历游标,默认是next表示升序;如果索引不是unique的,而你又不想访问重复的索引,可以使用nextunique或prevunique,这时每次会得到key最小的那个数据
dbIndex.openCursor(range, "prev").onsuccess = e =>    
    let cursor = e.target.result;
    if (cursor) 
        let data = cursor.value  // 数据的处理就在这里。。。 [ 理解 cursor.key,cursor.primaryKey,cursor.value ]
        cursor.continue()
     else 
        // 游标遍历结束!    
    

        需要说明的是 IDBKeyRange.bound([min1, min2, min3], [max1, max2, max3])   的范围如下:

max1 > min1 || max1 === min1 && max2 > min2 || max1 === min1 && max2 === min2 && max3 > min3  // 好好理解一下这个 bound 的含义吧 ! 

        11、使用promise封装上述方法

        a.使用promise封装 open方法

/**
 * 打开/创建数据库
 * @param object dbName 数据库的名字
 * @param string storeName 仓库名称
 * @param string version 数据库的版本
 * @param string keyPath 主键键值,不传就自动创建主键
 * @param Array index 索引数组
 * @return object 该函数会返回一个数据库实例
 */
type StoreOptions = 
  autoIncrement: boolean;
  keyPath?: string;
;
export const openDB = function (
  dbName: string,
  version: number,
  storeName: string,
  keyPath?: string,
  index?: Array<any[]> | undefined
) 
  return new Promise((resolve, reject) => 
    //  兼容浏览器
    // eslint-disable-next-line @typescript-eslint/ban-ts-comment
    // @ts-ignore
    const  webkitIndexedDB, indexedDB, mozIndexedDB, msIndexedDB  = window;
    const indexDB = indexedDB || mozIndexedDB || webkitIndexedDB || msIndexedDB;
    let db = null;
    const request = indexDB.open(dbName, version);
    // 操作成功
    request.onsuccess = function (event: any) 
      db = event?.target?.result; // 数据库对象
      resolve( code: 0, success: true, data: db, msg: "数据库打开成功!" );
    ;
    // 操作失败
    request.onerror = function () 
      resolve( code: -1, success: false, data: null, msg: "数据库打开失败!" );
    ;
    // 创建表和索引
    request.onupgradeneeded = function (event: any) 
      // 数据库创建或升级的时候会触发
      db = event?.target?.result; // 数据库对象
      const storeOptions: StoreOptions = 
        autoIncrement: true,
      ;
      if (keyPath && keyPath !== "") 
        storeOptions.autoIncrement = false;
        storeOptions.keyPath = keyPath;
      
      // 创建表
      if (!db.objectStoreNames.contains(storeName)) 
        const store = db.createObjectStore(storeName, storeOptions);
        // 创建索引
        // indexName索引列名称
        // indexKey索引键值
        if (index && index.length > 0) 
          index.forEach((item: any) => 
            if (
              !item.indexName ||
              !item.indexKey ||
              item.options.unique === undefined
            ) 
              reject(
                "索引格式错误,请参照格式indexName:'indexName',indexKey:'indexKey',unique: false"
              );
            
            store.createIndex(item.indexName, item.indexKey, item.options);
          );
        
      
    ;
  );
;

        b.使用promise封装 add方法 

/**
 * 新增数据
 * @param object db 数据库实例
 * @param string storeName 仓库名称
 * @param object dataConfig 添加的数据集合
 **/
export const addData = function (db: any, storeName: string, dataConfig: any) 
  return new Promise((resolve, reject) => 
    if (!db) 
      reject("数据库不存在或没有初始化");
    
    if (!dataConfig || !dataConfig.value) 
      reject("value是必传项,参照格式[keyPath]:'key',value:'value'");
    
    const req = db
      .transaction([storeName], "readwrite")
      .objectStore(storeName) // 仓库对象
      .add(dataConfig);
    // 操作成功
    req.onsuccess = function () 
      resolve( code: 0, success: true, data: null, msg: "数据写入成功!" );
    ;
    // 操作失败
    req.onerror = function () 
      const data = 
        code: -1,
        success: false,
        data: null,
        msg: "数据写入失败!",
      ;
      resolve(data);
    ;
  );
;

        c.使用promise封装 put方法 

/**
 * 更新数据
 * @param object db 数据库实例
 * @param string storeName 仓库名称
 * @param object dataConfig 更新的数据集合
 */
export const updateData = function (
  db: any,
  storeName: string,
  dataConfig: any
) 
  return new Promise((resolve, reject) => 
    if (!db) 
      reject("数据库不存在或没有初始化");
    
    if (!dataConfig || !dataConfig.value) 
      reject("value是必传项,参照格式[keyPath]:'key',value:'value'");
    
    const req = db
      .transaction([storeName], "readwrite")
      .objectStore(storeName)
      .put(dataConfig);
    // 操作成功
    req.onsuccess = function () 
      resolve( code: 0, success: true, data: null, msg: "数据更新成功!" );
    ;
    // 操作失败
    req.onerror = function () 
      const data = 
        code: -1,
        success: false,
        data: null,
        msg: "数据更新失败!",
      ;
      resolve(data);
    ;
  );
;

        d.使用promise封装 get方法

/**
 * 查询数据
 * @param object db 数据库实例
 * @param string storeName 仓库名称
 * @param string key 数据主键
 **/
export const getData = function (db: any, storeName: string, key: string) 
  return new Promise((resolve, reject) => 
    if (!db) 
      reject("数据库不存在或没有初始化");
    
    const req = db
      .transaction([storeName], "readonly")
      .objectStore(storeName) // 仓库对象
      .get(key);
    // 操作成功
    req.onsuccess = function (e:  target:  result: any  ) 
      resolve(
        code: 0,
        success: true,
        data: e?.target?.result,
        msg: "数据获取成功!",
      );
    ;
    // 操作失败
    req.onerror = function () 
      const data = 
        code: -1,
        success: false,
        data: null,
        msg: "数据获取失败!",
      ;
      resolve(data);
    ;
  );
;

        d.使用promise封delete方法 

/**
 * 删除数据
 * @param object db 数据库实例
 * @param string storeName 仓库名称
 * @param string key 数据主键
 **/
export const deleteData = function (db: any, storeName: string, key: string) 
  return new Promise((resolve, reject) => 
    if (!db) 
      reject("数据库不存在或没有初始化");
    
    const req = db
      .transaction([storeName], "readwrite")
      .objectStore(storeName) // 仓库对象
      .delete(key);
    // 操作成功
    req.onsuccess = function (e:  target:  result: any  ) 
      resolve(
        code: 0,
        success: true,
        data: e?.target?.result,
        msg: "数据删除成功!",
      );
    ;
    // 操作失败
    req.onerror = function () 
      const data = 
        code: -1,
        success: false,
        data: null,
        msg: "数据删除失败!",
      ;
      resolve(data);
    ;
  );
;

        e.使用promise封装 游标查询方法 

/**
 * 使用游标查询数据
 * @param object db 数据库实例
 * @param string storeName 仓库名称
 * @param string indexKey 查询的索引的键值
 * @param string index 查询的索引值
 **/
export const getIndexData = function (
  db: any,
  storeName: string,
  indexKey: string,
  index: string
) 
  return new Promise((resolve, reject) => 
    if (!db) 
      reject("数据库不存在或没有初始化");
    
    const keyRange = IDBKeyRange.only(index);
    const req = db
      .transaction([storeName], "readonly")
      .objectStore(storeName) // 仓库对象
      .index(indexKey)
      .openCursor(keyRange, "next");
    // 操作成功
    req.onsuccess = function (e:  target:  result: any  ) 
      resolve(
        code: 0,
        success: true,
        data: e?.target?.result,
        msg: "数据查询成功!",
      );
    ;
    // 操作失败
    req.onerror = function () 
      const data = 
        code: -1,
        success: false,
        data: null,
        msg: "数据查询失败!",
      ;
      resolve(data);
    ;
  );
;

        详细封装文件查看请戳链接:

https://gitee.com/yinlingyun123/dragVue/blob/master/src/utils/indexDB.tsxhttps://gitee.com/yinlingyun123/dragVue/blob/master/src/utils/indexDB.tsx

indexDB常见错误:

        1.transaction出错

Uncaught DOMException: Failed to execute ‘transaction’ on ‘IDBDatabase’: A version change transaction is running.

        错误原因:

        有一个事务在运行,不能打开第二个事务.

        解决办法:

        将transaction相关的代码写在objectStore.transaction.oncomplete中

var request = indexedDB.open('sql', 2);
request.onupgradeneeded = function(event) 
    var db = event.target.result;
    var objectStore = db.createObjectStore("customers",  keyPath: "ssn" );
    objectStore.transaction.oncomplete = function(event) 
        var customerObjectStore = db.transaction("customers", "readwrite")
        .objectStore("customers")
        .add( id: "555-55-5555", name: "Donna", age: 32, email: "donna@home.org" );
    ;
;

        2.Cannot read错误

Uncaught TypeError: Cannot read property ‘xxx’ of undefined

        错误原因:

        因为indexDB代码都是异步代码,所以有些还没有定义部分的代码可能会优先执行,导致这个变量为undefined

        3.尝试读取数据时候报错

Uncaught InvalidStateError: Failed to read the ‘result’ property from ‘IDBRequest’: The request has not finished

        错误示范:

var r = indexedDB.open();
var db = null;
r.onsuccess = function(event)  db = event.target.result); 

        正确示范:

var r = indexedDB.open();
r.onsuccess = function(event) 
    var db = event.target.result;
;

        4.Put的时候报错

failed to execute ‘put’ on ‘idbobjectstore’ evaluating the object store’s key path did not yield a value

        错误原因:        

        储存数据的时候必须要带上object store的key一起储存,或者使用一个key:generator(autoIncrement: true)

        示例:

var store = db.createObjectStore('my_store', keyPath: 'key');
store.put(key: 11, value: 33); // OK
store.put(value: 66); // throws, since 'key' is not present
var store = db.createObjectStore('my_store', keyPath: 'key', autoIncrement: true);
store.put(key: 11, value: 33); // OK, key generator set to 11
store.put(value: 66); // OK, will have auto-generated key 12

        5.createObjectStore undefined

Cannot read property ‘createObjectStore’ of undefined

       错误原因:   

        这是因为indexedDB是异步的,你必须在回调函数中使用createObjectStore方法,即使你把createObjectStore调用写在open函数后面,也无法保证哪个先完成。

       6.Failed to execute ‘createObjectStore’

Failed to execute ‘createObjectStore’ on ‘IDBDatabase’: The database is not running a version change transaction.

       错误原因:           

        这是由于在success事件的回调中调用createObjectStore方法,该方法应该在upgradeneeded事件的回调中调用。

        7.stores was not found.

Failed to exectue ‘transaction’ on ‘IDBDatabase’: One of the specified stores was not found.

        错误原因:          

        这是因为upgradeneeded事件没有被触发。

        这里需要注意upgradeneeded事件。首先,根据API,应该在upgradneeded事件的回调函数中调用createObjectStore方法创建store object,不应该在success的回调中,否则会报错。其次,当为open方法传入一个本域没有的数据库名时,会创建相应的数据库,并触发success、upgradeneeded事件,从而创建一个store object。但是,chrome54并不会触发upgradeneeded事件,造成store object不会被创建,后续在store object上创建事务并操作数据时候就会报错。Stackoverflow上提供的解决办法是,在open方法传入第二个参数(与已有version不同,且更大),这样就会触发chrome上的upgradeneeded事件了。不过,每次都需要调用db.version获取当前的版本号。

        8.add出现错误

Failed to execute 'add' on 'IDBObjectStore': The transaction has finished.

        错误原因:

        transaction结束了,不能继续使用,所以才需要每次操作数据库都要获得一个

其他

        IDBOpenDBRequest还有一个类似回调函数句柄——onupgradeneeded。

        该句柄在我们请求打开的数据库的版本号和已经存在的数据库版本号不一致的时候调用。

        indexedDB.open方法还有第二个可选参数,数据库版本号,数据库创建的时候默认版本号为1,当我们传入的版本号和数据库当前版本号不一致的时候onupgradeneeded就会被调用,当然我们不能试图打开比当前数据库版本低的version.

        代码中定义了一个myDB对象,在创建indexedDB request的成功毁掉函数中,把request获取的DB对象赋值给了myDB的db属性,这样就可以使用myDB.db来访问创建的indexedDB了。

        用indexedBD的时候要善用onerror来获取错误的信息,这样就知道哪里出错了。

参考文章链接:

前端存储之indexedDB

在前一个阶段的工作中,项目组要开发一个平台,为了做出更好的用户体验,实现快速、高质量的交互,从而更快得到用户的反馈,要求在前端把数据存储起来,之后我去研究了下现在比较流行的前端存储数据库,找到了indexedDB,于是便对indexedDB做了一个较为深入的探索,此文就是记录探索过程的一些心得体会。

indexedDB为何物

在使用一个技术之前,先搞清楚它是什么,这对你的理解很重要,从DB就可以看出,它肯定是一个数据库,而说到数据库,有两种不同类型的数据库,就是关系型数据库和非关系型数据库,关系型数据库如Mysql、Oracle等将数据存储在表中,而非关系型数据库如Redis、MongoDB等将数据集作为个体对象存储。indexedDB就是一个非关系型数据库,它不需要你去写一些特定的sql语句来对数据库进行操作,因为它是nosql的,数据形式使用的是json,

indexedDB出现的意义

也许熟悉前端存储的会说,不是有了LocalStorage和Cookies吗?为什么还要推出indexedDB呢?其实对于在浏览器里存储数据,你可以使用cookies或local storage,但它们都是比较简单的技术,而IndexedDB提供了类似数据库风格的数据存储和使用方式。

首先说说Cookies,英文直接翻译过来就是小甜点,听起来很好吃,实际上并不是,每次HTTP接受和发送都会传递Cookies数据,它会占用额外的流量。例如,如果你有一个10KB的Cookies数据,发送10次请求,那么,总计就会有100KB的数据在网络上传输。Cookies只能是字符串。浏览器里存储Cookies的空间有限,很多用户禁止浏览器使用Cookies。所以,Cookies只能用来存储小量的非关键的数据。

其次说说LocalStorage,LocalStorage是用key-value键值模式存储数据,但跟IndexedDB不一样的是,它的数据并不是按对象形式存储。它存储的数据都是字符串形式。如果你想让LocalStorage存储对象,你需要借助JSON.stringify()能将对象变成字符串形式,再用JSON.parse()将字符串还原成对象。但如果要存储大量的复杂的数据,这并不是一种很好的方案。毕竟,localstorage就是专门为小数量数据设计的,所以它的api设计为同步的。而IndexedDB很适合存储大量数据,它的API是异步调用的。IndexedDB使用索引存储数据,各种数据库操作放在事务中执行。IndexedDB甚至还支持简单的数据类型。IndexedDB比localstorage强大得多,但它的API也相对复杂。对于简单的数据,你应该继续使用localstorage,但当你希望存储大量数据时,IndexedDB会明显的更适合,IndexedDB能提供你更为复杂的查询数据的方式。

indexedDB的特性

1.对象仓库

有了数据库后我们自然希望创建一个表用来存储数据,但indexedDB中没有表的概念,而是objectStore,一个数据库中可以包含多个objectStore,objectStore是一个灵活的数据结构,可以存放多种类型数据。也就是说一个objectStore相当于一张表,里面存储的每条数据和一个键相关联。我们可以使用每条记录中的某个指定字段作为键值(keyPath),也可以使用自动生成的递增数字作为键值(keyGenerator),也可以不指定。选择键的类型不同,objectStore可以存储的数据结构也有差异。

键类型

存储数据

不使用

任意值,但是没添加一条数据的时候需要指定键参数

keyPath

任意值,但是没添加一条数据的时候需要指定键参数

keyGenerator

任意值

都使用

Javascript对象,如果对象中有keyPath指定的属性则不生成新的键值,如果没有自动生成递增键值,填充keyPath指定属性

 

如上图,有一个用于保存person的object Store,这个仓库的键就是person的ID值。

2. 事务性

在indexedDB中,每一个对数据库操作是在一个事务的上下文中执行的。事务范围一次影响一个或多个object stores,你通过传入一个object store名字的数组到创建事务范围的函数来定义。例如:db.transaction(storeName, \'readwrite\'),创建事务的第二个参数是事务模式。当请求一个事务时,必须决定是按照只读还是读写模式请求访问。

3. 基于请求

对indexedDB数据库的每次操作,描述为通过一个请求打开数据库,访问一个object store,再继续。IndexedDB API天生是基于请求的,这也是API异步本性指示。对于你在数据库执行的每次操作,你必须首先为这个操作创建一个请求。当请求完成,你可以响应由请求结果产生的事件和错误。

4. 异步

在IndexedDB大部分操作并不是我们常用的调用方法,返回结果的模式,而是请求—响应的模式,所谓异步API是指并不是这条指令执行完毕,我们就可以使用request.result来获取indexedDB对象了,就像使用ajax一样,语句执行完并不代表已经获取到了对象,所以我们一般在其回调函数中处理。

indexedDB怎么玩

IndexedDB 鼓励使用的基本模式如下所示:

  1. 打开数据库并且开始一个事务。
  2. 创建一个 object store。
  3. 构建一个请求来执行一些数据库操作,像增加或提取数据等。
  4. 通过监听正确类型的 DOM 事件以等待操作完成。
  5. 在操作结果上进行一些操作(可以在 request 对象中找到)

接下来如果想要理解indexedDB具体怎么玩,最好的方法就是创建一个简单的web应用:把人的姓名、电话、地址存储在IndexedDB里。IndexedDB里提供了简单的增、删、改、查接口,界面如下:

 

1.打开数据库

a)  首先,你需要知道你的浏览器是否支持IndexedDB。

var indexedDB = window.indexedDB || window.webkitIndexedDB || window.mozIndexedDB || window.msIndexedDB;
if(!indexedDB)
{
    console.log("你的浏览器不支持IndexedDB");
}

b)  创建请求打开indexedDB:一旦你的浏览器支持IndexedDB,我们就可以打开它。你不能直接打开IndexedDB数据库。IndexedDB需要你创建一个请求来打开它。

var request = indexedDB.open(name, version);

第一个参数是数据库的名称,第二个参数是数据库的版本号。版本号可以在升级数据库时用来调整数据库结构和数据。但你增加数据库版本号时,会触发onupgradeneeded事件,这时可能会出现成功、失败和阻止事件三种情况:

request.onerror = function(e) {
        console.log(e.currentTarget.error.message);
    };

    request.onsuccess = function(e) {
        myDB.db = e.target.result;
        console.log(\'成功打开DB\');
    };

    request.onupgradeneeded = function(e) {
        var db = e.target.result;
        if (!db.objectStoreNames.contains(\'person\')) {
            console.log("我需要创建一个新的存储对象");
            //如果表格不存在,创建一个新的表格(keyPath,主键 ; autoIncrement,是否自增),会返回一个对象(objectStore)
            var objectStore = db.createObjectStore(\'person\', {
                keyPath: "id",
                autoIncrement: true
            });

            //指定可以被索引的字段,unique字段是否唯一

            objectStore.createIndex("name", "name", {
                unique: false
            });

            objectStore.createIndex("phone", "phone", {
                unique: false
            });

        }
        console.log(\'数据库版本更改为: \' + version);
};

onupgradeneeded事件在第一次打开页面初始化数据库时会被调用,或在当有版本号变化时。所以,你应该在onupgradeneeded函数里创建你的存储数据。如果没有版本号变化,而且页面之前被打开过,你会获得一个onsuccess事件。

2. 添加数据

a)  首先需要创建一个事务,并要求具有读写权限

var transaction = db.transaction(storeName, \'readwrite\');

b)  获取objectStore,再调用add方法添加数据

var store = transaction.objectStore(storeName);

    var request = store.get(key);

    request.onsuccess = function(e) {

        data = e.target.result;

        console.log(student.name);

};

3.删除数据

删除跟新增一样,需要创建事务,然后调用删除接口,通过key删除对象。

var transaction = db.transaction(storeName, \'readwrite\');

    var store = transaction.objectStore(storeName);

store.delete(key);

4.查找数据

a)  按key查找

开启事务,获取objectStore,调用往get()方法,往方法里传入对象的key值,取出相应的对象

var transaction = db.transaction(storeName, \'readwrite\');

    var store = transaction.objectStore(storeName);

    var request = store.get(key);

    request.onsuccess = function(e) {

        data = e.target.result;

        console.log(student.name);

};

b)  使用索引查找

我们可以在创建object store的时候指明索引,使用object store的createIndex创建索引,方法有三个参数:索引名称、索引属性字段名、索引属性值是否唯一。

objectStore.createIndex("name", "name", {
                unique: false

            });

如上代码中,我们建好了name索引,就可以用该索引来进行查询了:

var transaction = db.transaction(storeName);

    var store = transaction.objectStore(storeName);

    var index = store.index(search_index);

    index.get(value).onsuccess = function(e) {

        data = e.target.result;

        console.log(student.id);

}

c)  游标遍历数据

对数据库熟悉的同学很好理解游标的作用,有了数据库object store的游标,我们就可以利用游标遍历object store了。

var transaction = db.transaction(storeName);

    var store = transaction.objectStore(storeName);

    var request = store.openCursor();//打开游标

    var dataList = new Array();

    var i = 0;

    request.onsuccess = function(e) {

        var cursor = e.target.result;

        if (cursor) {

            console.log(cursor.key);

            dataList[i] = cursor.value;

            console.log(dataList[i].name);

            i++;

            cursor.continue();

        }

        data = dataList;

};

4.更新对象

更新对象,首先要把它取出来,修改,然后再放回去。

var transaction = db.transaction(storeName, \'readwrite\');

    var store = transaction.objectStore(storeName);

    var request = store.get(key);

    request.onsuccess = function(e) {

        var data = e.target.result;

        for (a in newData) {

            //除了keypath之外

 

            data.a = newData.a;

        }

        store.put(data);

};

5.关闭与删除数据库

关闭数据库可以直接调用数据库对象的close方法

function closeDB(db) {

    db.close();

}

删除数据库使用数据库对象的deleteDatabase方法

function deleteDB(name) {

    indexedDB.deleteDatabase(name);

}

总结

以上就是indexedDB的一些基本概念以及使用,由于篇幅原因,还有一些更深入的细节没有介绍,比如indexedDB的游标结合索引,发挥其真正的优势,有兴趣的小伙伴可以继续深入研究,还有就是要注意浏览器的支持问题,IE9以及更早的版本并不支持,火狐和谷歌浏览器没有问题,推荐使用,文章如果纰漏或者不足,欢迎指正~

以上是关于前端数据库indexedDB入门的主要内容,如果未能解决你的问题,请参考以下文章

前端存储之indexedDB

前端存储之Web Sql Database

indexedDB bootstrap angularjs之 MVC Demo

indexedDB介绍

初探IndexedDB

web网站使用indexedDB缓存大数量案例