IndexedDB

1、概述

随着浏览器的功能不断增强,越来越多的网站开始考虑,将大量数据储存在客户端,这样可以减少从服务器获取数据,直接从本地获取数据。

现有的浏览器数据储存方案,都不适合储存大量数据:Cookie 的大小不超过4KB,且每次请求都会发送回服务器;LocalStorage 在 2.5MB 到 10MB 之间(各家浏览器不同),而且不提供搜索功能,不能建立自定义的索引。所以,需要一种新的解决方案,这就是 IndexedDB 诞生的背景。

通俗地说,IndexedDB 就是浏览器提供的本地数据库,它可以被网页脚本创建和操作。IndexedDB 允许储存大量数据,提供查找接口,还能建立索引。这些都是 LocalStorage 所不具备的。就数据库类型而言,IndexedDB 不属于关系型数据库(不支持 SQL 查询语句),更接近 NoSQL 数据库。

2、特点

(1)键值对储存。 IndexedDB 内部采用对象仓库(object store)存放数据。所有类型的数据都可以直接存入,包括 JavaScript 对象。对象仓库中,数据以"键值对"的形式保存,每一个数据记录都有对应的主键,主键是独一无二的,不能有重复,否则会抛出一个错误。

(2)异步。 IndexedDB 操作时不会锁死浏览器,用户依然可以进行其他操作,这与 LocalStorage 形成对比,后者的操作是同步的。异步设计是为了防止大量数据的读写,拖慢网页的表现。

(3)支持事务。 IndexedDB 支持事务(transaction),这意味着一系列操作步骤之中,只要有一步失败,整个事务就都取消,数据库回滚到事务发生之前的状态,不存在只改写一部分数据的情况。

(4)同源限制 IndexedDB 受到同源限制,每一个数据库对应创建它的域名。网页只能访问自身域名下的数据库,而不能访问跨域的数据库。

(5)储存空间大 IndexedDB 的储存空间比 LocalStorage 大得多,一般来说不少于 250MB,甚至没有上限。 

(6)支持二进制储存。 IndexedDB 不仅可以储存字符串,还可以储存二进制数据(ArrayBuffer 对象和 Blob 对象)。

IndexedDB 数据库的使用目前可以直接在 HTTP 协议下使用,这个和 cacheStorage 缓存存储必须使用 HTTPS 协议不一样。所以就应用场景来讲,IndexedDB 数据库还是挺广的,考虑到 IE10 也支持,所以基本可以确定在实际项目中应用是绝对不成问题的。

例如,页面中一些不常变动的结构化数据,我们就可以使用 IndexedDB 数据库存储在本地,有助于增强页面的交互性能。

3、操作流程

3.1、打开数据库

使用 IndexedDB 的第一步是打开数据库,使用indexedDB.open()方法。

var request = window.indexedDB.open(databaseName, version);

这个方法接受两个参数,第一个参数是字符串,表示数据库的名字。如果指定的数据库不存在,就会新建数据库。第二个参数是整数,表示数据库的版本。如果省略,打开已有数据库时,默认为当前版本;新建数据库时,默认为1

indexedDB.open()方法返回一个 IDBRequest 对象。这个对象通过三种事件errorsuccessupgradeneeded,处理打开数据库的操作结果。

(1)error 事件

error事件表示打开数据库失败。


request.onerror = function (event) {
  console.log('数据库打开报错');
};

(2)success 事件

success事件表示成功打开数据库。


var db;

request.onsuccess = function (event) {
  db = request.result;
  console.log('数据库打开成功');
};

这时,通过request对象的result属性拿到数据库对象。

(3)upgradeneeded 事件

如果指定的版本号,大于数据库的实际版本号,就会发生数据库升级事件upgradeneeded


var db;

request.onupgradeneeded = function (event) {
  db = event.target.result;
}

这时通过事件对象的target.result属性,拿到数据库实例。

(4)例子

var dbName = 'patrolLine';
    var dbVersion = 1;
    var db;
    // 打开数据库
    function openIDB(params) {
        params = params || {};
        /**
         * 第一个参数是字符串,表示数据库的名字。
         * 如果指定的数据库不存在,就会新建数据库。
         *
         * 第二个参数是整数,表示数据库的版本。
         * 如果省略,打开已有数据库时,默认为当前版本;新建数据库时,默认为1
         */
        var request = window.indexedDB.open(dbName, dbVersion);

        // 成功打开数据库,假如说打开的是已经存在的数据库,那么触发的是onsuccess函数
        request.onsuccess = function (event) {
            db = request.result;
            console.log('数据库打开成功');
            params.onsuccess && params.onsuccess(event)
        };

        // 数据库升级事件,当创建一个新的数据库或者增加已经存在的数据库版本号时,onupgrageneeded事件会被触发。
        request.onupgradeneeded = function (event) {
            db = event.target.result;
            params.onupgradeneeded && params.onupgradeneeded(event)

            var objectStore;
            if (!db.objectStoreNames.contains('violationrecords')) {
                objectStore = db.createObjectStore('violationrecords', { keyPath: 'id', autoIncrement: true });
                // 建立索引
                // objectStore.createIndex('name', 'name', { unique: false });
            }
        }
        // 打开数据库失败
        request.onerror = function (event) {
            console.log('数据库打开报错');
            params.onerror && params.onerror(event)
        };
    }

3.2 、新建数据库

新建数据库与打开数据库是同一个操作。如果指定的数据库不存在,就会新建。不同之处在于,后续的操作主要在upgradeneeded事件的监听函数里面完成,因为这时版本从无到有,所以会触发这个事件。

通常,新建数据库以后,第一件事是新建对象仓库(即新建表)。


request.onupgradeneeded = function(event) {
  db = event.target.result;
  var objectStore = db.createObjectStore('person', { keyPath: 'id' });
}

上面代码中,数据库新建成功以后,新增一张叫做person的表格,主键是id

更好的写法是先判断一下,这张表格是否存在,如果不存在再新建。


request.onupgradeneeded = function (event) {
  db = event.target.result;
  var objectStore;
  if (!db.objectStoreNames.contains('person')) {
    objectStore = db.createObjectStore('person', { keyPath: 'id' });
  }
}

主键(key)是默认建立索引的属性。比如,数据记录是{ id: 1, name: '张三' },那么id属性可以作为主键。主键也可以指定为下一层对象的属性,比如{ foo: { bar: 'baz' } }foo.bar也可以指定为主键。

如果数据记录里面没有合适作为主键的属性,那么可以让 IndexedDB 自动生成主键。


var objectStore = db.createObjectStore(
  'person',
  { autoIncrement: true }
);

上面代码中,指定主键为一个递增的整数

新建对象仓库以后,下一步可以新建索引。


request.onupgradeneeded = function(event) {
  db = event.target.result;
  var objectStore = db.createObjectStore('person', { keyPath: 'id' });
  objectStore.createIndex('name', 'name', { unique: false });
  objectStore.createIndex('email', 'email', { unique: true });
}

上面代码中,IDBObject.createIndex()的三个参数分别为索引名称、索引所在的属性、配置对象(说明该属性是否包含重复的值)。

3.3 、新增数据

新增数据指的是向对象仓库写入数据记录。这需要通过事务完成。

function add() {
  var request = db.transaction(['person'], 'readwrite')
    .objectStore('person')
    .add({ id: 1, name: '张三', age: 24, email: 'zhangsan@example.com' });

  request.onsuccess = function (event) {
    console.log('数据写入成功');
  };

  request.onerror = function (event) {
    console.log('数据写入失败');
  }
}

add();

上面代码中,写入数据需要新建一个事务。新建时必须指定表格名称和操作模式("只读"或"读写")。新建事务以后,通过IDBTransaction.objectStore(name)方法,拿到 IDBObjectStore 对象,再通过表格对象的add()方法,向表格写入一条记录。

写入操作是一个异步操作,通过监听连接对象的success事件和error事件,了解是否写入成功。

例子:

function add() {
        var request = db.transaction(['violationrecords'], 'readwrite')
            .objectStore('violationrecords')
            .add({ name: '张三', age: 24, email: 'zhangsan@example.com' });

        request.onsuccess = function (event) {
            console.log('数据写入成功');
        };

        request.onerror = function (event) {
            console.log('数据写入失败');
        }
    }

3.4 、读取数据

读取数据也是通过事务完成。 


function read() {
   var transaction = db.transaction(['person']);
   var objectStore = transaction.objectStore('person');
   var request = objectStore.get(1);

   request.onerror = function(event) {
     console.log('事务失败');
   };

   request.onsuccess = function( event) {
      if (request.result) {
        console.log('Name: ' + request.result.name);
        console.log('Age: ' + request.result.age);
        console.log('Email: ' + request.result.email);
      } else {
        console.log('未获得数据记录');
      }
   };
}

read();

上面代码中,objectStore.get()方法用于读取数据,参数是主键的值。 

3.5 、遍历数据

遍历数据表格的所有记录,要使用指针对象 IDBCursor。


function readAll() {
  var objectStore = db.transaction('person').objectStore('person');

   objectStore.openCursor().onsuccess = function (event) {
     var cursor = event.target.result;

     if (cursor) {
       console.log('Id: ' + cursor.key);
       console.log('Name: ' + cursor.value.name);
       console.log('Age: ' + cursor.value.age);
       console.log('Email: ' + cursor.value.email);
       cursor.continue();
    } else {
      console.log('没有更多数据了!');
    }
  };
}

readAll();

上面代码中,新建指针对象的openCursor()方法是一个异步操作,所以要监听success事件。

例子

    // 游标查询所有
    function readAll() {
        var objectStore = db.transaction('violationrecords').objectStore('violationrecords');

        objectStore.openCursor().onsuccess = function (event) {
            var cursor = event.target.result;

            if (cursor) {
                console.log('Id: ' + cursor.key);
                console.log('Name: ' + cursor.value.name);
                console.log('Age: ' + cursor.value.age);
                console.log('Email: ' + cursor.value.email);
                cursor.continue();
            } else {
                console.log('没有更多数据了!');
            }
        };
    }

3.6 、更新数据

更新数据要使用IDBObject.put()方法。


function update() {
  var request = db.transaction(['person'], 'readwrite')
    .objectStore('person')
    .put({ id: 1, name: '李四', age: 35, email: 'lisi@example.com' });

  request.onsuccess = function (event) {
    console.log('数据更新成功');
  };

  request.onerror = function (event) {
    console.log('数据更新失败');
  }
}

update();

上面代码中,put()方法自动更新了主键为1的记录

例子:

    // 更新
    function update() {
        var request = db.transaction(['violationrecords'], 'readwrite')
            .objectStore('violationrecords')
            .put({ id: 1, name: '李四', age: 35, email: 'lisi@example.com' });

        request.onsuccess = function (event) {
            console.log('数据更新成功');
        };

        request.onerror = function (event) {
            console.log('数据更新失败');
        }

  3.7 、删除数据

IDBObjectStore.delete()方法用于删除记录。


function remove() {
  var request = db.transaction(['person'], 'readwrite')
    .objectStore('person')
    .delete(1);

  request.onsuccess = function (event) {
    console.log('数据删除成功');
  };
}

remove();

例子:

    // 删除 
    function remove() {
        var request = db.transaction(['violationrecords'], 'readwrite')
            .objectStore('violationrecords')
            .delete(1);

        request.onsuccess = function (event) {
            console.log('数据删除成功');
        };
    }

 3.8 、使用索引

索引的意义在于,可以让你搜索任意字段,也就是说从任意字段拿到数据记录。如果不建立索引,默认只能搜索主键(即从主键取值)。

假定新建表格的时候,对name字段建立了索引。

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

现在,就可以从name找到对应的数据记录了。


var transaction = db.transaction(['person'], 'readonly');
var store = transaction.objectStore('person');
var index = store.index('name');
var request = index.get('李四');

request.onsuccess = function (e) {
  var result = e.target.result;
  if (result) {
    // ...
  } else {
    // ...
  }
}

例子:

    function serachIndex(params) {
        params = params || {};
        var request = window.indexedDB.open(['person'], dbVersion);

        request.onsuccess = function (event) {
            db = request.result;
            console.log('person数据库打开成功');
            params.onsuccess && params.onsuccess(event)
        };

        request.onupgradeneeded = function (event) {
            db = event.target.result;
            params.onupgradeneeded && params.onupgradeneeded(event)

            var objectStore;
            if (!db.objectStoreNames.contains('person')) {
                objectStore = db.createObjectStore('person', { keyPath: 'id', autoIncrement: true });
                //建立索引
                objectStore.createIndex('name', 'name', { unique: false });
            }
        }

        request.onerror = function (event) {
            console.log('person数据库打开报错');
            params.onerror && params.onerror(event)
        };

        //插入数据
        var request1 = db.transaction(['person'], 'readwrite')
            .objectStore('person')
            .add({ name: '张三', age: 24, email: 'zhangsan@example.com' });

        request1.onsuccess = function (event) {
            console.log('数据写入成功');
        };

        request1.onerror = function (event) {
            console.log('数据写入失败');
        }

        //更新数据
        var request2 = db.transaction(['person'], 'readwrite')
            .objectStore('person')
            .put({ id: 1, name: '李四', age: 35, email: 'lisi@example.com' });

        request2.onsuccess = function (event) {
            console.log('数据更新成功');
        };

        request2.onerror = function (event) {
            console.log('数据更新失败');
        }

        //使用索引查询
        var transaction = db.transaction(['person'], 'readonly');
        var store = transaction.objectStore('person');
        var index = store.index('name');
        var request = index.get('李四');

        request.onsuccess = function (e) {
            var result = e.target.result;
            if (result) {
                console.log(result);
            } else {
                console.log('使用索引查询失败');
            }
        }
    }

参考链接:

http://www.ruanyifeng.com/blog/2018/07/indexeddb.html

https://www.cnblogs.com/guxingzhe/p/13213615.html

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值