不一样IndexedDB包装器让你起飞

611 阅读4分钟

这是MDN原文对IndexedDB的一种描述:

IndexedDB 是一种底层 API,用于在客户端存储大量的结构化数据(也包括文件/二进制大型对象(blobs))。该 API 使用索引实现对数据的高性能搜索。虽然 Web Storage 在存储较少量的数据很有用,但对于存储更大量的结构化数据来说力不从心。而 IndexedDB 提供了这种场景的解决方案。

IndexedDB 是一个事务型数据库系统,类似于基于 SQL 的 RDBMS。然而,不像 RDBMS 使用固定列表,IndexedDB 是一个基于 JavaScript 的面向对象数据库。IndexedDB 允许您存储和检索用键索引的对象;可以存储结构化克隆算法支持的任何对象。您只需要指定数据库模式,打开与数据库的连接,然后检索和更新一系列事务。

developer.mozilla.org/zh-CN/docs/…

特性

  • 仅支持浏览器客户端
  • 支持异步、同步(最新规范已不推荐)两种Api
  • 数据库绑定域名
  • 基于Web Worker的线程运行
  • 具备数据库的基本特征
  • 存储空间上限为浏览器的最高配额

浏览器的最大存储空间是动态的——它取决于您的硬盘大小。 全局限制为可用磁盘空间的 50%。在 Firefox 中,一个名为 Quota Manager 的内部浏览器工具会跟踪每个源用尽的磁盘空间,并在必要时删除数据。因此,如果您的硬盘驱动器是 500GB,那么浏览器的总存储容量为 250GB。如果超过此范围,则会发起称为源回收的过程,删除整个源的数据,直到存储量再次低于限制。删除源数据没有只删一部分的说法——因为这样可能会导致不一致的问题。

花式变脸

本文不探讨 IndexedDB 的标准 API,我们主要了解一下基于 IndexedDB 为基础,这些年技术开发者通过包装实现的方式,产生了一些优秀的工具库。

包括 localForage、dexie.js、PouchDB、idb、idb-keyval、JsStore 或者 lovefield 等,当开发者使用这些库时,会很自然的感受到其友好、强大的一面,不仅仅实现了简洁的键值对接口,更令人惊叹是部分包装器支持了类似 NoSQL 和 SQL语法 的操作形式,无论您习惯于哪种数据库的操作方式,都可以顺利上手。

为了启发思考,我们重点介绍其中三款具有代表性的 IndexedDB 封装库。

1. localForage

一个简单的 Polyfill,提供了简单的客户端数据存储的值语法,基于key-value的形式来操作存储。localForage 有一个优雅降级策略,若浏览器不支持 IndexedDB 或 WebSQL,则使用 localStorage。在所有主流浏览器中都可用:Chrome,Firefox,IE 和 Safari(包括 Safari Mobile)。

localforage.config({
  driver      : localforage.INDEXEDDB, // Force IndexedDB; same as using setDriver()
  name        : 'mydb',
  version     : 1.0,
  storeName   : 'keyvaluepairs', // Should be alphanumeric, with underscores.
  description : 'some description'
});
localforage.setItem('key', 'value', function (err) {
  // if err is non-null, we got an error
  localforage.getItem('key', function (err, value) {
    // if err is non-null, we got an error. otherwise, value is the value
  });
});
2. ZangoDB

ZangoDB是IndexedDB的一个类似MongoDB的接口,支持MongoDB的大多数常见过滤、投影、排序、更新和聚合功能。

let db = new zango.Db('mydb', { people: ['age'] });
let people = db.collection('people');

let docs = [
    { name: 'Frank', age: 20 },
    { name: 'Thomas', age: 33 },
    { name: 'Todd', age: 33 },
    { name: 'John', age: 28 },
    { name: 'Peter', age: 33 },
    { name: 'George', age: 28 }
];

people.insert(docs).then(() => {
    return people.find({
        name: { $ne: 'John' },
        age: { $gt: 20 }
    }).group({
        _id: { age: '$age' },
        count: { $sum: 1 }
    }).project({
        _id: 0,
        age: '$_id.age'
    }).sort({
        age: -1
    }).forEach(doc => console.log('doc:', doc));
}).catch(error => console.error(error));
//out put
doc: { count: 3, age: 33 }
doc: { count: 1, age: 28 }
3. JsStore

JsStore 是一个 IndexedDB 包装器。它通过类似 SQL 的 api 让 IndexedDB 变得超级简单,其数据库模式是一个包含数据库名称和表列表的对象。

// initiate jsstore connection
var connection = new JsStore.Connection();

// step1 - create database schema
var tblProduct = {
    name: 'Product',
    columns: {
        // Here "Id" is name of column 
        Id:{ primaryKey: true, autoIncrement: true },
        ItemName:  { notNull: true, dataType: "string" },
        Price:  { notNull: true, dataType: "number" },
        Quantity : { notNull: true, dataType: "number" }
    }
};

var tblOrder = {
    name: 'Order',
    columns: {
        // Here "OrderId" is name of column 
        OrderId:{ primaryKey: true, autoIncrement: true }
    }
};

var db = {
      name: dbName,
      tables: [tblProduct, tblOrder]
}

// step 2
var isDbCreated = await connection.initDb(db);
// isDbCreated will be true when database will be initiated for first time

if(isDbCreated){
    alert('Db Created & connection is opened');
}
else{
    alert('Connection is opened');
}

// since connection is opened now, you can call apis.
Copy 
var value = {
    column1: value1,
    column2: value2,
    column3: value3,
    ...
    columnN: valueN
};

var noOfRowsInserted = await connection.insert({
    into: "TABLE_NAME",
    values: [Value], //you can insert multiple values at a time
});
if (noOfRowsInserted > 0) {
    alert('Successfully Added');
}
Copy 
var results = await connection.select({
    from: table1 name,
    where: {
        [column name]: some value
    },
    join: {
        with: table2_name,
        on: "table1.common_field=table2.common_field",
        type:"inner",
        where: {
            [column name]: some value
        }
    }
});
console.log(results);

PS:IndexedDB 的包装器,如同裁缝为不同顾客定制的服装。面对复杂的应用程序,我们可以根据开发者的工作习惯和需求,定制出不同形式的API,就像裁缝为不同体型的客户量身定做合身的衣物。这种“量体裁衣”的设计思想,可以让开发者获得更好的使用体验,提高工作效率。代码世界日新月异,但优秀的工具向来本于用户,立足实用,始终不忘初心。