初识IndexedDB

253 阅读6分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第5天,点击查看活动详情

一. 前言

目前很多浏览器端据存储方案都不适合存储大量数据,例如Cookies容量不超过4KB,localStorage一般浏览器支持是5M大小,对前端大数据存储有点力不从心。为此,浏览器端的数据存储需要种新的技术方案,IndexedDB应运而生。

IndexedDB是HTML5提供的内置于浏览器中的数据库,它可以通过网页脚本程序创建和操作。IndexedDB允许储存大量数据,并且提供查询接口建立索引的功能。就数据库类型而言,IndexedDB不属于关系型数据库(不支持SQL查询语句),更接近文档型数据库。

二. indexedDB特点

(1) 键值对储存
IndexedDB内部采用对象仓库(ObjectStore)存放数据,所有类型的数据都可以直接存入包括JavaScript对象。在对象仓库中,数据以“键值对”的形式保存,每一个数据都有对应的键名且键名必须是唯一的,否则会抛出错误。

(2) 异步API
IndexedDB数据库在执行增、删、改、查操作时不会锁死浏览器,用户依然可以进行其他操作。与localStorage的同步设计相比,IndexedDB的异步设计可以防止大量数据读写时拖慢网页加载速度,而影响用户的网站体验。

(3) 支持事务
IndexedDB支持事务,所以一系列操作步骤中,只要有一步失败,这个事务就都取消,数据库回滚到事务发生之前的状态。举个例子,一次操作需要在一 个数据表中同时插入两条数据,第1条数据插入成功,第2条数据插入失败。那么,对于整个操作来说,两条数据都插入成功才算成功,失败时便需要事务的回滚,将已经插入的第1条数据清除。

(4) 同域限制
IndexedDB也受到同域限制,每-一个数据库对应创建该数据库的域名,来自不同域名的网页只能访问自身域名下的数据库,而不能访问其他域名下的数据库。

(5) 存储空间大
IndexedDB的存储空间比localStorage大得多,一般来说不少于250MB。不同浏览器的限制不同,IE的储存上限是250MB,Chrome和Opera浏览器的存储上限是硬盘剩余空间的某个百分比,Firefox浏览器则没有上限。

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

三. 基本使用

3.1 打开/新建数据库

const indexedDB = window.indexedDB || window.webkitindexedDB || window.msIndexedDB || window.mozIndexedDB; //兼容多种浏览器
var request = indexedDB.open(databaseName, version);

databaseName:数据库名称,如果指定数据库不存在,则会新建该名称的数据库
version:整数,表示数据库的版本号。打开已有数据库默认为当前版本。新建数据库时,默认版本号为1。
indexedDB.open()方法返回一个 IDBRequest 对象。这个对象通过以下三种事件处理打开数据库的操作结果。

(1)onerror:打开失败

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

(2)onsuccess:打开成功

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

(3)onupgradeneeded:据库版本升级或创建数据库时触发,在该事件中创建数据表

var db;
request.onupgradeneeded = function (event) {
  db = event.target.result;
  if (!db.objecttables.contains('table')) { //判断数据库中是否已经存在该名称的数据表
    objectStore = db.createObjectStore('table', { keyPath: 'id' });  //主键为id
    objectStore.createIndex('age', 'age', { unique: true }); //添加索引,三个参数分别表示为 索引名称,索引所在的属性,该属性是否包含相同的值
  }
}

3.2 新增数据

function addData() {
  var request = db.transaction(['table'], 'readwrite') //readwrite表示有读写权限
    .objectStore('table')
    .add({ id: 1, name: '小明', age:18}); //新增数据
  request.onsuccess = function (event) {
    console.log('数据写入成功');
  };
  request.onerror = function (event) {
    console.log('数据写入失败');
  }
}
addData();

3.3 修改数据

function updateData() {
  let request = db.transaction(['table'], 'readwrite') //readwrite表示有读写权限
    .objectStore('table')
    .put({ id: 1, name: '小明', age: 24}); //新增数据
  request.onsuccess = function (event) {
    console.log('数据写入成功');
  };
  request.onerror = function (event) {
    console.log('数据写入失败');
  }
}
updateData();

3.4 读取数据

function read() {
   const transaction = db.transaction(['table']);
   const objectStore = transaction.objectStore('table');
   const request = objectStore.get(1); //通过主键获取对应数据
   
   //也可以使用索引自定义字段进行搜索
   // let index = objectStore.index('name');
   // const request = index.get("小明");
   
   request.onerror = function(event) {
     console.log('数据读取失败');
   };
   request.onsuccess = function( event) {
      if (request.result) {
        console.log(request.result);
      } else {
        console.log('未获得数据记录');
      }
   };
}
read();

3.5 删除数据

function removeData() {
  let request = db.transaction(['table'], 'readwrite')
    .objectStore('table')
    .delete(1);
  request.onsuccess = function (event) {
    console.log('数据删除成功');
  };
}
removeData();
//通过游标和索引删除数据
function cursorDeleteData(){
    //通过游标删除记录
    let store = db.transaction(['table'],'readwrite').objectStore('table');
    let request = store.openCursor();
    request.onsuccess = function(e){
        let cursor = e.target.result,
            deleteRequest;
        if(cursor){
          deleteRequest = cursor.delete();//请求删除当前项
          deleteRequest.onerror = function(){
            console.log('游标删除该记录失败');
          };
          cursor.continue();
        }
    };
}

3.6 清空表所有数据

function clearData(){
    //删除存储空间全部记录
    let store = db.transaction(['table'],'readwrite').objectStore('table');
    let request = store.clear();
    request.onerror = function () {
       //....
    }
    request.onsuccess = function () {
       //....
    }
}
clearData();

3.7 删除数据库

indexedDB.deleteDatabase(DatabaseName); 

四. indexedDB封装与实践

export default {
  // indexedDB兼容
  indexedDB: window.indexedDB || window.webkitindexedDB || window.msIndexedDB || window.mozIndexedDB,

  // 打开数据库
  // 新对象储存空间newStore参数:{name: "model",keyPath: "panelId"}
  openDB: function (dbname, version, newStore) { 
    return new Promise(resolve=>{
      let newVersion = version || 1;
      let request = this.indexedDB.open(dbname, newVersion); //数据库名、版本号
      request.onerror = function () {
        console.log("IndexedDB数据库打开失败")
      }
      request.onsuccess = function (event) {
        let db = event.target.result;
        resolve(db)
      }
      // onupgradeneeded,调用创建新的储存空间(版本升级也会触发)
      request.onupgradeneeded = function (event) {
        let db = event.target.result;
        if (newStore) {
          if (!db.objectStoreNames.contains(newStore.name)) {
            //新建一个model表格,主键为panelId
            let objectStore = db.createObjectStore(newStore.name, {
              keyPath: newStore.keyPath //主键keyPath='panelId'
            })
            console.log(objectStore)
            //这里可以添加索引
            // objectStore.createIndex('email', 'email', { unique: true });
          }
        }
      }
    })
  },
  //删除数据库
  deleteDB: function (dbname) {
    return new Promise(resolve=>{
      let deleteQuest = this.indexedDB.deleteDatabase(dbname);
      deleteQuest.onerror = function () {
        console.log("删除数据库出错")
        resolve(false)
      }
      deleteQuest.onsuccess = function () {
        console.log("删除数据库成功")
        resolve(true)
      }
    })
  },
  //关闭数据库
  closeDB: function (db) {
    db.close();
    console.log("数据库已关闭")
  },

  //写入数据
  addData: async function(db, storeName, objValue) {
    if(!db){
      db = await this.openDB("modelData",1,{name:'model',keyPath:'panelId'});
    }
    let request = db.transaction(storeName, 'readwrite').objectStore(storeName).add(objValue)
    request.onerror = function () {
      // console.log("数据写入失败/已存在")
    }
    request.onsuccess = function () {
      // console.log("数据写入成功")
    }
  },

  //根据板件id更新数据
  updateData: function (db, storeName, panelId, obj) {
    let transaction = db.transaction(storeName, 'readwrite');
    let store = transaction.objectStore(storeName);
    let request = store.get(panelId); //根据主键删除
    return new Promise((resolve, reject) => {
      request.onsuccess = function (e) {
        let stocktable = e.target.result;
        if (stocktable) {
          stocktable.modeldata = obj
          resolve(store.put(stocktable)); //IDBObject.put()方法
        } else {
          reject(false);
        }
      }
    })
  },

  //删除数据
  deleteData: function (db, storename, panelId) {
    let store = db.transaction(storename, 'readwrite').objectStore(storename);
    let request = store.delete(panelId); //IDBObjectStore.delete()
    return new Promise(resolve => {
      request.onerror = function () {
        console.log("删除失败");
        resolve(false)
      }
      request.onsuccess = function () {
        console.log("删除成功")
        resolve(true)
      }
    })
  },

  //清空表所有数据
  clearStoreData: async function (db, storename) {
    if(!db){
      db = await this.openDB("modelData",1,{name:'model',keyPath:'panelId'});
    }
    let store = db.transaction(storename, 'readwrite').objectStore(storename);
    let request = store.clear();
    return new Promise(resolve => {
      request.onerror = function () {
        resolve(false)
      }
      request.onsuccess = function () {
        resolve(true)
      }
    })
  },


  //读取数据
  read: function (db, storename, panelId) {
    let store = db.transaction(storename, 'readwrite').objectStore(storename);
    let request = store.get(panelId); //通过主键获取对应数据objectStore.get()
    return new Promise(resolve => {
      request.onerror = function () {
        console.log("单条数据读取失败")
        resolve(false)
      }
      request.onsuccess = function (event) {
        resolve(event.target.result);
      }
    })
  },

  //读取所有数据
  readAll: function (db, storename) {
    var objectStore = db.transaction(storename).objectStore(storename);
    let request = objectStore.openCursor(); //openCursor()遍历数据
    var dataList = [];
    return new Promise((resolve) => {
      request.onerror = function () {
        console.log("单条数据读取失败")
        resolve(false)
      }
      request.onsuccess = function (event) {
        var cursor = event.target.result;
        if (cursor) {
          dataList.push(cursor.value)
          cursor.continue();
        } else {
          resolve(dataList);
        }
      };
    })
  },

  //通过索引查找信息
  readDataByIndexes: function (db, storename, indexValue,queryData) {
    let store = db.transaction(storename, 'readonly').objectStore(storename);
    let index = store.index(indexValue); //xxx值初始化数据库onupgradeneeded新建的索引objectStore.createIndex
    let request = index.get(queryData);
    return new Promise(resolve => {
      request.onerror = function () {
        console.log("根据索引读取数据失败")
        resolve(false)
      }
      request.onsuccess = function (event) {
        resolve(event.target.result);
      }
    })
  }

}
//调用
import myDB from "@/assets/js/indexedDB.js";
this.IndexedDB = await myDB.openDB("modelData", 1, {
     name: "model",
     keyPath: "panelId",
});