緩存 - 數據緩存 - 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.?


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM