缓存 - 数据缓存 - IndexedDB - Dexie.js

Classes

  • Dexie
  • DexieError
  • Collection
    • and():Add JS based criteria to collection(向集合添加基于JS的条件)
    • delete():Delete all objects in the collection(删除集合中的全部对象)
    • distinct():Remove duplicates of items with same primary key(删除具备相同主键的项的重复项)
    • modify():Modify all objects in the collection with given properties or function.(使用给定的属性或函数修改集合中的全部对象。)
    • ————————————————————————————————————————————————————————————————————
    • each():Execute query and call a function for each item(执行查询并为每一个项调用函数)
    • eachKey():Execute query on the index or primary key being used and call a function for each key(对正在使用的索引或主键执行查询,并为每一个键调用函数)
    • eachPrimaryKey():Execute query on the index and call a function for each primary key that corresponds to the index.(对索引执行查询,并为对应于索引的每一个主键调用函数。)
    • eachUniqueKey():Execute query on the index or primary key being used and call a function for each unique key(对正在使用的索引或主键执行查询,并为每一个惟一键调用函数)
    • ————————————————————————————————————————————————————————————————————
    • toArray():Execute query and get an array with the results sorted by the index used in the where() clause(执行查询并获取一个数组,其结果按where()子句中使用的索引排序)
    • desc():Sort in descending order(按降序排序)
    • reverse():Reverse the order of items.(颠倒项目顺序。)
    • sortBy():Execute query and get an array with the results sorted by given property(执行查询并获取按给定属性排序结果的数组)
    • ————————————————————————————————————————————————————————————————————
    • first():Get the first item in the collection
    • last():Get the last item in the collection(获取集合中的最后一项)
    • limit():Limit the result to given number of items(将结果限制为给定的项目数)
    • offset():Ignore N items before given offset and return the rest(忽略给定偏移量以前的n个项目并返回其他项目)
    • until():Ignores items occurring after given filter returns true.(忽略给定筛选器返回true以后发生的项。)
    • filter():Filter objects
    • ————————————————————————————————————————————————————————————————————
    • keys():Retrieve an array containing all keys of the collection (index or primary key depending on where() clause)(检索包含集合全部键的数组(索引键或主键取决于where()子句))
    • primaryKeys():Retrieve an array containing all primary keys of the collection(检索包含集合的全部主键的数组)
    • uniqueKeys():Retrieve an array containing all unique keys of the collection (index or primary key depending on where() clause)(检索包含集合全部惟一键的数组(索引键或主键取决于where()子句))
    • ————————————————————————————————————————————————————————————————————
    • count():Get the number of items in the collection(获取集合中的项数)
    • or():Logical OR operation
    • raw():Don’t filter results through reading hooks(不要经过阅读挂钩过滤结果)
    • clone():Clone the query before manipulating it further (Does not clone database items).(在进一步操做以前克隆查询(不克隆数据库项)。)
  • IndexSpec
  • Promise
  • Table
    • name:The name of the object store represented by this Table instance.
    • schema:The table schema of this object store.(此对象存储区的表架构。)
    • ————————————————————————————————————————————————————————————————————
    • hook(‘creating’):Atomic CRUD hook called when object is about to be created in db.(当对象将要在数据库中建立时调用)
    • hook(‘reading’):Atomic CRUD hook called when object has been read from db and is about to be delivered to caller.
    • hook(‘updating’):Atomic CRUD hook called when object is about to be modified in db.
    • hook(‘deleting’):Atomic CRUD hook called when object is about to be deleted from db.
    • ————————————————————————————————————————————————————————————————————
    • toArray():Get an array containing all objects in store.(获取包含存储区中全部对象的数组。)
    • limit():Return a Collection ordered by primary key, limited to N items.(返回按主键排序的集合,限制为n个项目。)
    • orderBy():Returns a Collection instance ordered by given index.(返回按给定索引排序的集合实例。)
    • offset():Return a Collection ordered by primary key, where the first N items in the table are ignored.(返回按主键排序的集合,其中表中的前n个项将被忽略。)
    • reverse():Returns a Collection instance with reversed order of the primary key.(返回主键顺序相反的集合实例。)
    • ————————————————————————————————————————————————————————————————————
    • get():Retrieve object by primary key.
    • where():Retrieve objects using a query.(使用查询检索对象。)
    • add():Insert an object into store.
    • bulkAdd():Same as add() but takes array arguments and is optimized for adding a large number of objects.(与add()相同,但接受数组参数,并针对添加大量对象进行了优化。)
    • delete():Delete an object from store.
    • bulkDelete():Same as delete() but takes and array of keys and is optimized for deleting a large number of objects.
    • put():Replace or insert object.(替换或插入对象。)
    • bulkPut():Same as put() but takes array arguments and is optimized for putting a large number of objects.
    • update():Apply given changes to an existing object.(将给定的更改应用于现有对象。)
    • clear():Clear all objects in store.
    • ————————————————————————————————————————————————————————————————————
    • defineClass():Define a javascript constructor function and map to this table.(映射表)
    • mapToClass():Map this table to javascript constructor function.(映射表)
    • count():Count all objects.
    • each():Iterate all objects in store.(迭代存储区中的全部对象。)
    • filter():Apply javascript filter on all items in the object store
    • toCollection():Get a Collection containing all objects in store.(获取包含存储区中全部对象的集合。)
  • TableSchema
  • Transaction
  • Version
  • WhereClause
    • above():Returns a collection of objects where index is above given key(返回索引位于给定键上方的对象集合)
    • aboveOrEqual():Returns a collection of objects where index is above or equal given key(返回索引高于或等于给定键的对象集合)
    • ————————————————————————————————————————————————————————————————————
    • below():Returns a collection of objects where index is below given key(返回索引低于给定键的对象集合)
    • belowOrEqual():Returns a collection of objects where index is below or equal given key(返回索引低于或等于给定键的对象集合)
    • ————————————————————————————————————————————————————————————————————
    • between():Returns a collection of objects where index is between given boundaries(返回索引位于给定边界之间的对象集合)
    • inAnyRange():Returns a collection where index is within any of the given ranges.(返回索引在任何给定范围内的集合)
    • ————————————————————————————————————————————————————————————————————
    • noneOf():Returns a collection where index equals anything but any of the keys in given array(返回一个集合,其中index等于给定数组中的任何键之外的任何值)
    • notEqual():Returns a collection where index equals anything but given value(返回一个集合,其中索引等于给定值之外的任何值)
    • ————————————————————————————————————————————————————————————————————
    • equalsIgnoreCase():Returns a collection of objects where index equals given string-key ignoring case differences(返回一个对象集合,其中索引等于给定的字符串键,忽略大小写差别)
    • equals():Returns a collection of objects where index equals given key(返回索引等于给定键的对象集合)
    • ————————————————————————————————————————————————————————————————————
    • anyOfIgnoreCase():Returns a collection of objects where index matches any of given strings, ignoring case differences.(返回一个对象集合,其中索引与任何给定字符串匹配,忽略大小写差别。)
    • anyOf():Returns a collection of objects where index is equal to any of the keys in given array(返回一个对象集合,其中索引等于给定数组中的任何键)
    • ————————————————————————————————————————————————————————————————————
    • startsWith():Returns a collection of objects where index starts with given string-key(返回以给定字符串键开头的对象集合)
    • startsWithIgnoreCase():Returns a collection of objects where index starts with given string-key ignoring case differences(返回一个对象集合,其中索引以给定的字符串键开头,忽略大小写差别)
    • ————————————————————————————————————————————————————————————————————
    • startsWithAnyOf():Returns a collection of objects where index starts with any of the given strings(返回以任何给定字符串开头的对象集合)
    • startsWithAnyOfIgnoreCase():Returns a collection of objects where index starts with any of given strings, ignoring case differences(返回一个对象集合,其中索引以任何给定字符串开头,忽略大小写差别)

Quick Reference(快速参考)

——————————————————————————————————————————————

总体说明

  • meaning that any operation that requires a result won’t be returned directly. Instead all such operations will return a Promise.(任何须要结果的操做都不会同步返回,全部这样的行为都将返回 Promise)
  • But Dexie gives you a little shortcut in all methods returning a promise with a value, so the above code will be equal to:(Dexie 为全部返回值的异步方法提供了简便方法,能够不写Promise成功时的then而改成回调函数传入方法中)
db.friends.where('name').startsWithIgnoreCase('arnold').toArray().then(function(a) {...})
// 等价于
db.friends.where('name').startsWithIgnoreCase('arnold').toArray(function(a) { // 回调函数传入toArray这个异步方法
    console.log(a.length);
}).catch(function(err) {
    console.error(err);
});

——————————————————————————————————————————————

安装

npm install dexie --save

——————————————————————————————————————————————

Declare Database(声明数据库)

  • An instance of Dexie will represent a database connection(一个Dexie 实例就是一个database 对象)
  • On your Dexie instance you will have direct access to instances of Table for each object store you have defined in your schema.(在Dexie 实例下能够直接经过属性访问定义好的各个表实例)
  • You only need to specify properties that you wish to index. The object store will allow any properties on your stored objects but you can only query them by indexed properties(只须要指定索引字段,value能够保存任意属性)
  • Never index properties containing images, movies or large (huge) strings. Store them in IndexedDB, yes! but just don’t index them!(不要把任何大数据做为索引对象,包括图片、视频、长字符串)
import Dexie from 'dexie';
var db = new Dexie("MyDatabase");
db.version(1).stores({
    friends: "++id, name, age, *tags",
    gameSessions: "id, score"
});

Understanding the flow(流程)

  • First time ◦Database is being created(建立数据库)
    • If on(‘populate’) is triggered to populate ground data.(若是有populate,触发填充初始数据)
    • db.open() promise resolves.(db.open()调用then成功)
  • Modify Schema(修改表的架构)

——————————————————————————————————————————————

The populate Event(初始化事件)

  • If database is not present, or an earlier version was present, indexedDB’s onupgradeneeded event is fired and taken care of by Dexie.(若是数据库不存在或传入更高版本号,indexedDB的 onupgradeneeded事件将被触发)
  • IndexedDB is designed for handling database creation and upgrades through the onupgradeneeded event, and define the schema there.(indexeddb设计用于经过onupgradeneeded 事件处理数据库建立和升级,并在其中定义模式。)
  • Dexie adds a declarative schema syntax on top of that so that you don’t need to subscribe to the onupgradeneeded event either.(Dexie添加了一个声明性模式语法,这样您也不须要订阅onupgradened事件。)
  • The database schema is declarative, not imperative.(数据库表架构是声明性的,不是必需的。能够在打开数据库后再建立?)
  • In case your database need initial data in order to work - data that must only be populated on database creation and never more, you can subscribe to the populate event. (若是要在数据库创建时初始化数据可使用 populate 事件)
  • This will only be called in case the database is initially created - not when it is upgraded.(populate 事件只在创建数据库时调用,更新时不调用)
var db = new Dexie("MyTicketDB");

db.version(1).stores({
    tickets: "++id,headline,description,statusId",
    statuses: "++id,name,openess"
});

db.on("populate", function() {
    // Init your DB with some default statuses:
    db.statuses.add({id: 1, name: "opened", openess: true});
    db.statuses.add({id: 2, name: "closed", openess: false});
    db.statuses.add({id: 3, name: "resolved", openess: false});
    db.statuses.add({id: 4, name: "wontfix", openess: false});
});

——————————————————————————————————————————————

Schema Syntax(表模式的语法)

  • ++keyPathAutoincrement primary key(自动增长的主键)
  • ++Hidden autoincremented primary key(隐藏的自动增长的主键)
  • keyPathMeans that primary key can be any type and we have to provide it ourself(主键能够是任意值,新增时须要手动传入)keyPath represents a property name or a dotted path to a nested property.(keypath能够是属性名或嵌套属性的点路径。)
  • (blank)Hidden primary key(隐藏主键?)
  • &keyPathMeans that keyPath is indexed and keys must be unique(索引的值是惟一的)
  • *keyPathMeans that if key is an array, each array value will be regarded as a key to the object.(若是索引的值是一个数组,那么数组中的每一项都能索引到该行数据)This feature lacks support in IE.(此功能在IE中缺少支持)
  • [keyPath1+keyPath2]Defining a compound index for keyPath1 and keyPath2(以keyPath1 and keyPath2定义复合索引,和and查询相同效果)his feature lacks support in IE.(此功能在IE中缺少支持)

Indexable Types(可索引类型)

  • Only properties of certain types can be indexed. This includes string, number, Date and Array but NOT boolean, null or undefined. (能索引的类型:字符串、数值、日期、数组。不能索引的类型:布尔、null、undefined)
  • IndexedDB 2.0 contains support for indexing binary data. (IndexedDB 2.0支持二进制数据作为索引)
  • Indexing a property path that turns out to hold a non-indexable type will have no effect. (为一个属性路径创建索引,结果发现该属性路径包含一个不可索引的类型,将不会产生任何效果。)
  • 属性路径,例如:定义索引或主键为a.b,那么这张表就会认为value下的a是一个对象,而且这个对象有个属性b能够做为主键或索引。能够经过a.b这个路径直接操做表

——————————————————————————————————————————————

Class Binding(绑定类)

  • Whichever method you use, your database will return real instances of your mapped class, so that the expression(数据库获取数据操做返回的数据将是这个类的一个实例)
  • mapToClass() - map an existing class to an objectStore(将类映射到表)
class Friend {
    // Prototype method
    save() {
        return db.friends.put(this); // Will only save own props.
    }

    // Prototype property(原型属性)
    get age() { // 这里个get 是class中的一个关键字,new Friend().age时会调用该函数
        return moment(Date.now()).diff (this.birthDate, 'years'); // moment是一个日期处理库
    }
}

db.friends.mapToClass(Friend);
  • defineClass() - let Dexie declare a class for you(Dexie声明一个类)
var db = new Dexie("MyAppDB");

db.version(1).stores({
    folders: "++id,&path",
    files: "++id,filename,extension,folderId"
});

var Folder = db.folders.defineClass({
    id: Number,
    path: String,
    description: String
});

Folder.prototype.save = function () {
    return db.folders.put(this);
}

var File = db.files.defineClass({
    id: Number,
    filename: String,
    extension: String,
    folderId: Number,
    tags: [String]
});

File.prototype.save = function () {
    return db.files.put(this);
}

——————————————————————————————————————————————

Change Tracking(变更钩子)

  • With Dexie it’s possible to control and monitor each database change.(使用dexie,能够控制和监视每一个数据库更改。经过钩子函数)
  • Whenever database is about to be read from or modified, they allow hook implementation to modify what will happen, or just react on the event.(当数据库即将被读取或修改时触发)
  • Hooks Documentation(钩子列表)
    • hook(‘creating’)
    • hook(‘reading’)
    • hook(‘updating’)
    • hook(‘deleting’)

——————————————————————————————————————————————

打开数据库(非必须)

  • Dexie also supports queuing operations, meaning you can start using the database directly after having defined it. In case open() hasn’t been called, it will open it automatically and enqueue the operation to execute as soon as the database is finished opening. (Dexie 支持在实例化后链式调用请求,这些请求支持依序排队执行。这时若是open未被调用,将自动打开数据库执行这些排队任务)
  • If open fails, queued operations will immediately fail with the error event from the open request.(若是打开数据库失败,排队任务也将失败,错误信息显示在oepn方法的Promise.cacth中)
var db = new Dexie("FriendsAndPetsDB");

db.version(1).stores({
    friends: "++id,name,isCloseFriend", // 自增主键和索引名
    pets: "++id,name,kind"
});
db.open();
db.friends.add({name: "Ingemar Bergman", isCloseFriend: 0});
db.pets.add({name: "Josephina", kind: "dog", fur: "too long right now"});

——————————————————————————————————————————————

Upgrade(升级)

  • 下一个版本会基于上一个版本改变,因此升级时应当保持原有版本的数据库声明不变
  • If no database present, Dexie initializes the last version directly by parsing the stores schema syntax and adding stores and indexes accordingly. No upgrade() functions run in this case.(若是新建数据库,upgrade方法不会被触发)
  • If any error occur in any upgrade function in the sequence, the upgrade transaction will roll back and db.open() will fail. (若是序列中的任何升级函数发生错误,升级事务将回滚,db.open()将失败。)
db.version(1).stores({
    friends: "++id,name,age,*tags",
    gameSessions: "id,score"
});

db.version(2).stores({
    friends: "++id, [firstName+lastName], yearOfBirth, *tags", // Change indexes(改变索引)
    gameSessions: null // Delete table(删除表)

}).upgrade(tx => {
    // Will only be executed if a version below 2 was installed.(当前浏览器数据库版本低于2时触发)
    return tx.friends.modify(friend => { // tx.friends返回的是Collection类的实例?
        friend.firstName = friend.name.split(' ')[0];
        friend.lastName = friend.name.split(' ')[1];
        friend.birthDate = new Date(new Date().getFullYear() - friend.age, 0);
        delete friend.name; // delete是js中删除对象的关键字
        delete friend.age;
    });
});

——————————————————————————————————————————————

Add Items(添加)

  • 容许存储二级制数据,例如:new Uint8Array([1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16]
await db.friends.add({name: "Josephine", age: 21});

await db.friends.bulkAdd([
  {name: "Foo", age: 31},
  {name: "Bar", age: 32}
]);

——————————————————————————————————————————————

Update Items(更新)

// 替换或新增
await db.friends.put({id: 4, name: "Foo", age: 33});

await db.friends.bulkPut([
    {id: 4, name: "Foo2", age: 34},
    {id: 5, name: "Bar2", age: 44}
]);

// 根据主键更新(更新?)
await db.friends.update(4, {name: "Bar"});

// 根据搜索结果更新
await db.customers
    .where("age")
    .inAnyRange([ [0, 18], [65, Infinity] ]) // 返回索引在任何给定范围内的集合。
    .modify({discount: 0.5}); // 更新

——————————————————————————————————————————————

Delete items(删除)

// 根据主键删除
await db.friends.delete(4);

await db.friends.bulkDelete([1,2,4]);


// 根据搜索结果删除
const oneWeekAgo = new Date(Date.now() - 60*60*1000*24*7);
await db.logEntries
    .where('timestamp').below(oneWeekAgo)
    .delete();
  • 去重
db.users.where('email').startsWith('david@').distinct() // 删除具备相同主键的项的重复项?

——————————————————————————————————————————————

Query Items(查询)

  • 尽可能不使用传入函数的方法,应该效率会比较低
  • 查询方式有两种
    • Table.get() - retrieve an object by its primary key.(经过主键获取)
    • Table.where() - do an advanced query.(执行高级搜索)
  • Native indexedDB has no support for logical AND or OR operations.(原生indexedDB 不支持and 和 or 查询)
  • Logical OR cannot be done by filtering - we must query the database with two queries to get it.(or 是经过两次查询取并集得到的)
  • We would gain no performance by letting the database handle Logical AND (launching two separate queries and the filter away entries that don’t exist in both collections).(and 经过js过滤性能更好)
var db = new Dexie('music');
db.version(1).stores({
    genres: '++id,name',
    albums: '++id,name,year,*tracks',
    bands: '++id,name,*albumIds,genreId'
});

async function getBandsStartingWithA () {
    const bands = await db.bands
        .where('name')
        .startsWith('A') // 返回以给定字符串键开头的对象集合
        .toArray();

    await Promise.all (bands.map (async band => { // bands.map返回[promise, ... ]
      [band.genre, band.albums] = await Promise.all([
        db.genres.get (band.genreId), // 经过主键获取
        db.albums.where('id').anyOf(band.albumIds).toArray()
      ]);
    }));
    
    return bands;
}
db.users.where('name').startsWithIgnoreCase('da') // 返回一个对象集合,其中索引以给定的字符串键开头,忽略大小写差别

const abcFriends = await db.friends
    .where("name")
    .startsWithAnyOfIgnoreCase(["a", "b", "c"]) // 返回一个对象集合,其中索引以任何给定字符串开头,忽略大小写差别
    .toArray();
await db.customers
    .where("age")
    .inAnyRange([ [0, 18], [65, Infinity] ]) // 返回索引在任何给定范围内的集合。
    .modify({discount: 0.5});
db.friends
    .where('tags')
    .equals('close-friend')
    .primaryKeys(); // Retrieve an array containing all primary keys of the collection(返回查询结果的全部主键组成的数组)
const someFriends = await db.friends
    .where("age").between(20, 25) // 返回索引位于给定边界之间的对象集合
    .offset(150) // 忽略给定偏移量以前的n个项目并返回其他项目
    .limit(25) // 将结果限制为给定的项目数
    .toArray(); // 执行查询并获取一个数组,其结果按where()子句中使用的索引排序
db.friends.toCollection() // 返回表中全部项的集合。
    .modify(friend => { 
        // Modify each friend:
        friend.firstName = friend.name.split(' ')[0];
        friend.lastName = friend.name.split(' ')[1];
        delete friend.name;
    });
await db.friends
    .where("name").equalsIgnoreCase("josephine") // 返回一个对象集合,其中索引等于给定的字符串键,忽略大小写差别
    .each(friend => { // 执行查询并为每一个项调用函数
        console.log("Found Josephine", friend);
    });
const friendsContainingLetterA = await db.friends
    .filter(friend => /a/i.test(friend.name)) // 筛选对象
    .toArray();
await db.friends
    .where('age').above(25) // 返回age大于25
    .or('shoeSize').below(8) // 返回shoeSize小于8
    .or('interests').anyOf('sports', 'pets', 'cars') // 返回一个对象集合,其中索引等于给定数组中的任何键(数组?这个例子有问题吗?)
    .modify(friend => friend.tags.push("marketing-target")); // 使用给定的属性或函数修改集合中的全部对象。
db.friends.where('shoeSize')
    .between(37, 40)
    .or('name')
    .anyOf(['Arnold','Ingemar'])
    .and(function(friend) { return friend.isCloseFriend; }) // 传入过滤函数返回布尔值
    .limit(10)
    .each(function(friend){
        console.log(JSON.stringify(friend));
    });
const best5GameSession = await db.gameSessions
    .orderBy("score") // 根据score字段排序
    .reverse() // 颠倒项目顺序
    .limit(5) // 将结果限制为给定的项目数
    .toArray();
const forbundsKansler = await db.friends
    .where('[firstName+lastName]')
    .equals(["Angela", "Merkel"]) // 返回索引等于给定键的对象集合(复合索引时传入数组,其余传入对应数据类型)
    .first(); // 获取集合中的第一项
// In Dexie 2.0, you could do the above query a little simpler:(在新版本中可写为)
const forbundsKansler = await db.friends.where({
    firstName: "Angela",
    lastName: "Merkel"
}).first();
// 或
const forbundsKansler = await db.friends.get({ // 获取给定primarykey(主键)的对象或知足给定条件(keypath1:value1,keypath2:value2)的对象,并返回第一个匹配结果。
    firstName: "Angela",
    lastName: "Merkel"
});
// 等价于 select * from friends where firstName='Angela' order by lastName
// order by 排序
const angelasSortedByLastName = await db.friends
    .where('[firstName+lastName]')
    .between([["Angela", ""], ["Angela", "\uffff"])
    .toArray()

——————————————————————————————————————————————

Transaction(事务)

  • 事务能够集合多个数据库操做,这样包裹在函数中的数据库操做就能够实现复用
  • This will not only encapsulate your changes into an atomic operation, but also optimize your code! Internally, non-transactional operations also use a transaction but it is only used in the single operation, so if you surround your code within a transaction, you will perform less costly operations in total.(对数据库的操做都是经过事务完成的,只是分为显示和隐示。把多个操做封装为事务能够时多个操做统一回滚,而且提升性能)
  • If modifying database and any error occur, every modification will be rolled back.(经过事务操做数据库,当出现错误时可以自动回滚)
  • You may do all write operations synchronically without the need to wait for it to finish before starting the next one. (see the 2nd example code below).(能够同步执行全部写操做)
  • Even read-operations can be done the line after a write operations without waiting for write to finish - still your result will include all modifications. This is possible because all operations are queued when there is a pending write operation going on in current transaction.(事务中有写操做时,全部的操做都会排队执行,因此排在后面的读操做可以读取上一条写操做写入的数据)
  • "rw" should be replaced with "r" if you are just going to do read operations.(能够用‘r’替换'rw'若是读数据是只读操做)
await db.transaction('rw', [db.friends], async () => { // 注意:这里事务操做表的声明使用了数组
  // 注意这里的事务处理函数使用了异步函数
  const friend = await db.friends.get(1);
  ++friend.age;
  await db.friends.put(friend);
});
db.transaction('rw', db.friends, db.pets, function () {  // 注意:这里事务操做表的声明使用了多参数
     // 注意这里的事务处理函数使用了同步函数
    // MAIN transaction block(主事务)
    db.transaction('rw', db.pets, function () { // 事务嵌套,若是父事务回滚了,子事务会回滚吗?
       // SUB transaction block(子事务,事务能够嵌套)
    });
});
db.transaction('rw', db.friends, function() { // 注意:这里事务操做表的声明使用了单参数
    db.friends.add({id:1, name:"Fredrik"});
    db.friends.add({id:1, name:"Fredrik"}).catch(function (err) {
        // Adding same primary key twice will of course fail. (两次添加相同的主键会失败。)
        // But since we catch this error explicitely, the transaction wont abort. (可是,因为咱们捕获了这个错误,事务不会停止。)
        // This makes it possible to continue the transaction in a managed way.
        // If you still want to abort the transaction, just do Dexie.currentTransaction.abort(),(若是你想终止事务请执行Dexie.currentTransaction.abort())
        // throw an exception, or just:(从新抛出错误)
        // return Promise.reject(err);(返回Promise.reject(err))
    });
}).then (function () {
    alert ("Transaction successfully completed");
});
  • IndexedDB will commit a transaction as soon as it isn’t used within the same task.(indexeddb将在同一任务中不使用事务时当即提交该事务。)
  • News in Dexie 2.0.0-beta.6: You can now wait for other async APIs and still hold the transaction active, using Dexie.waitFor()(在Dexie 2.0.0-beta.6可使用Dexie.waitFor()保持事务打开状态,执行非数据库操做的异步方法)
  • Make sure to use the global promise (window.Promise) within transactions. (在事务中只能使用绑定在winodw下的全局Promise)
// 该例子是错误示范,应该先把import库绑定在window上,window.Promise = Promise
// promise-polyfill-of-your-choice是低版本浏览器promise的兼容库
import Promise form 'promise-polyfill-of-your-choice'; 

db.transaction(..., ()=>{
    Promise.all()
    Promise.race() // Promise.race([promise1,promise2]) // 其中一个promise返回就会触发Promise.race改变状态
    new Promise((resolve, reject) => { ... })
})
  • In the case you write a library (not an app) and you want your library to work on old browsers without requiring a Promise polyfill, it is still safe to use Dexie.Promise:(Dexie内部提供了Dexie.Promise来兼容低版本浏览器)
db.transaction(..., ()=>{
    Dexie.Promise.all()
    Dexie.Promise.race()
    new Dexie.Promise((resolve, reject) => { ... })
})

Exception Handling(异常处理)

  • When you work with transactions, you will also get the benefit of being able to catch all errors in one single place - at the end of the transaction, instead of having to catch() every promise of each database operation. (使用事务时不须要对事务中的全部数据库操做进行错误处理,事务的异常包含内部的全部错误)
  • Any uncatuch error (no matter error events, exception or miss-spelled variable in your code) will abort the ongoing Transaction and trigger its returned Promise to reject, waking up any catch() clause attached to the transaction scope.(任何错误都会终止事务)
  • If you catch a Promise from a database operation within a transaction, it will be considered to be handled and the transaction will not be aborted.(若是在事务中捕获了异常后,事务会认为异常已经被处理,并不会终止事务)
  • If not rethrowing the error, Nils would be successfully added and transaction would commit since the error is regarded as handled when you catch the database operation.(若是没有抛出错误,事务中其余数据库操做会被执行)
  • This could be a common pitfall when people catch promises within transactions just to log it but expecting the transaction to abort. Solution: re-throw the errors that you don’t handle!(若是只是为了记录错误,能够经过从新抛出错误来终止事务)

——————————————————————————————————————————————

疑问

  • db.diary.log 表对象(Table)彷佛没有这个方法?
db.transaction('rw', db.friends, db.diary, async () => {
    await spreadYourLove(); // spreadYourLove是另外一个事务
    await db.diary.log({date: Date.now(), text: "Today I successfully spread my love"}); // 这里的log很奇怪?
}).catch (err => {
    console.error ("I failed to spread my love :( " + err.stack);
});
  • catch的第一个参数为何是错误类型?
db.friends.where('name').startsWithIgnoreCase('arnold').toArray(function(a) {
    console.log(a.length);
}).catch(DOMError, function(e) {
    console.error("DOMError occurred: " + err);
}).catch(TypeError, function(e) {
    console.error("TypeError occurred: " + err);
}).catch(function(err) {
    console.error("Unknown error occurred: " + err);
}).finally(function(){
    console.log("Finally the query succeeded or failed.");
});
  • Dexie Promises supports a pattern similar to Thread-local storage where it is possible to have static properties that is bound to the executing promise and all it’s child-promises. This is similar Angular’s zone.js but in an unobtrusive way (no requirement of including any monkey-patching script). Dexie.js and it’s transaction API heavily depends on it’s transaction zones since it enables code to be aware of the currently executing transaction without having to pass transaction objects around.?
相关文章
相关标签/搜索