亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專(zhuān)欄INFORMATION COLUMN

IndexedDB 打造靠譜 Web 離線數(shù)據(jù)庫(kù)

孫吉亮 / 974人閱讀

摘要:設(shè)置為參數(shù)設(shè)置指定索引,并確保唯一性上面主要做了件事打開(kāi)數(shù)據(jù)庫(kù)表新建,并設(shè)置設(shè)置打開(kāi)數(shù)據(jù)庫(kù)表主要就是版本號(hào)和名字,沒(méi)有太多講的,我們直接從創(chuàng)建開(kāi)始吧。打開(kāi)注意事項(xiàng)檢查是否支持版本更新在生成一個(gè)實(shí)例時(shí),需要手動(dòng)指定一個(gè)版本號(hào)。

在知乎和我在平常工作中,常常會(huì)看到一個(gè)問(wèn)題:

前端現(xiàn)在還火嗎?

這個(gè)我只想說(shuō):

隔岸觀火的人永遠(yuǎn)無(wú)法明白起火的原因,只有置身風(fēng)暴,才能找到風(fēng)眼之所在 ——『秦時(shí)明月』

你 TM 看都不看前端現(xiàn)在的發(fā)展,怎么去評(píng)判前端火不火,我該不該嘗試一下其他方面的內(nèi)容呢?本人為啥為這么熱衷于新的技術(shù)呢?主要原因在于,生怕會(huì)被某一項(xiàng)顛覆性的內(nèi)容淘汰掉,從前沿領(lǐng)域掉隊(duì)下來(lái)。說(shuō)句人話就是:窮,所以只能學(xué)了...。所以本文會(huì)從頭剖析一下 IndexedDB 在前端里面的應(yīng)用的發(fā)展。

indexedDB 目前在前端慢慢得到普及和應(yīng)用。它正朝著前端離線數(shù)據(jù)庫(kù)技術(shù)的步伐前進(jìn)。以前一開(kāi)始是 manifest、localStorage、cookie 再到 webSQL,現(xiàn)在 indexedDB 逐漸被各大瀏覽器認(rèn)可。我們也可以針對(duì)它來(lái)進(jìn)行技術(shù)上創(chuàng)新的開(kāi)發(fā)。比如,現(xiàn)在小視頻非常流行,那么我們可以在用戶觀看時(shí),通過(guò) cacheStorage 緩存,然后利用 WebRTC 技術(shù)實(shí)現(xiàn) P2P 分發(fā)的控制,不過(guò)需要注意,一定要合理利用大小,不然后果真的很?chē)?yán)重。

indexedDB 的整體架構(gòu),是由一系列多帶帶的概念串聯(lián)而成,全部概念如下列表。一眼看去會(huì)發(fā)現(xiàn)沒(méi)有任何邏輯,不過(guò),這里我順手畫(huà)了一幅邏輯圖,中間會(huì)根據(jù) 函數(shù) 的調(diào)用而相互串聯(lián)起來(lái)。

IDBRequest

IDBFactory

IDBDatabase

IDBObjectStore

IDBIndex

IDBKeyRange

IDBCursor

IDBTransaction

整體邏輯圖如下:

TL;DR

下文主要介紹了 indexedDB 的基本概念,以及在實(shí)際應(yīng)用中的實(shí)操代碼。

indexedDB 基礎(chǔ)概念。在 indexedDB 里面會(huì)根據(jù)索引 index 來(lái)進(jìn)行整體數(shù)據(jù)結(jié)構(gòu)的劃分。

indexedDB 數(shù)據(jù)庫(kù)的更新是一個(gè)非常蛋疼的事情,因?yàn)?,Web 的靈活性,你既需要做好向上版本的更新,也需要完善向下版本的容錯(cuò)性。

indexedDB 高效索引機(jī)制,在內(nèi)部,indexedDB 已經(jīng)提供了 index、cursor等高效的索引機(jī)制,推薦不要直接將所有數(shù)據(jù)都取回來(lái),再進(jìn)行篩選,而是直接利用 cursor 進(jìn)行。

最后推薦幾個(gè)常用庫(kù)

離線存儲(chǔ)

IndexedDB 可以存儲(chǔ)非常多的數(shù)據(jù),比如 Object,files,blobs 等,里面的存儲(chǔ)結(jié)構(gòu)是根據(jù) Database 來(lái)進(jìn)行存儲(chǔ)的。每個(gè) DB 里面可以有不同的 object stores。具體結(jié)構(gòu)如下圖:

并且,我們可以給 key 設(shè)定相關(guān)特定的值,然后在索引的時(shí)候,可以直接通過(guò) key 得到具體的內(nèi)容。使用 IndexDB 需要注意,其遵循的是同域原則。

indexDB 基本概念

在 indexDB 中,有幾個(gè)基本的操作對(duì)象:

Database: 通過(guò) open 方法直接打開(kāi),可以得到一個(gè)實(shí)例的 DB。每個(gè)頁(yè)面可以創(chuàng)建多個(gè) DB,不過(guò)一般都是一個(gè)。

idb.open(name, version, upgradeCallback)

Object store: 這個(gè)就是 DB 里面具體存儲(chǔ)的對(duì)象。這個(gè)可以對(duì)應(yīng)于 SQL 里面的 table 內(nèi)容。其存儲(chǔ)的結(jié)構(gòu)為:

index: 有點(diǎn)類(lèi)似于外鏈,它本身是一種 Object store,主要是用來(lái)在本體的 store 中,索引另外 object store 里面的數(shù)據(jù)。需要區(qū)別的是,key 和 index 是不一樣的??梢詤⒖迹?index DEMO,mdn index。如下圖表示:

如下 code 為:

// 創(chuàng)建 index
var myIndex = objectStore.index("lName"); 

transaction: 事務(wù)其實(shí)就是一系列 CRUD 的集合內(nèi)容。如果其中一個(gè)環(huán)節(jié)失敗了,那么整個(gè)事務(wù)的處理都會(huì)被取消。例如:

var trans1 = db.transaction("foo", "readwrite");
var trans2 = db.transaction("foo", "readwrite");
var objectStore2 = trans2.objectStore("foo")
var objectStore1 = trans1.objectStore("foo")
objectStore2.put("2", "key");
objectStore1.put("1", "key");

cursor: 主要是用來(lái)遍歷 DB 里面的數(shù)據(jù)內(nèi)容。主要是通過(guò) openCursor 來(lái)進(jìn)行控制。

function displayData() {
  var transaction = db.transaction(["rushAlbumList"], "readonly");
  var objectStore = transaction.objectStore("rushAlbumList");

  objectStore.openCursor().onsuccess = function(event) {
    var cursor = event.target.result;
    if(cursor) {
      var listItem = document.createElement("li");
      listItem.innerHTML = cursor.value.albumTitle + ", " + cursor.value.year;
      list.appendChild(listItem);  

      cursor.continue();
    } else {
      console.log("Entries all displayed.");
    }
  };
}
如何使用 IndexDB

上面說(shuō)了幾個(gè)基本的概念。那接下來(lái)我們實(shí)踐一下 IndexDB。實(shí)際上入門(mén) IndexDB 就是做幾個(gè)基本的內(nèi)容

打開(kāi)數(shù)據(jù)庫(kù)表

設(shè)置指定的 primary Key

定義好索引的 index

前期搭建一個(gè) IndexedDB 很簡(jiǎn)單的代碼如下:

var request = indexedDB.open(dbName, 2);

request.onerror = function(event) {
  // 錯(cuò)誤處理程序在這里。
};
request.onupgradeneeded = function(event) {
  var db = event.target.result;
  // 設(shè)置 id 為 primaryKey 參數(shù)
  var objectStore = db.createObjectStore("customers", { keyPath: "id",{autoIncrement:true} });
  
  // 設(shè)置指定索引,并確保唯一性
  objectStore.createIndex("name", "name", { unique: false });
  objectStore.createIndex("email", "email", { unique: true });

};

上面主要做了 3 件事:

打開(kāi)數(shù)據(jù)庫(kù)表

新建 Store,并設(shè)置 primary Key

設(shè)置 index

打開(kāi)數(shù)據(jù)庫(kù)表主要就是版本號(hào)和名字,沒(méi)有太多講的,我們直接從創(chuàng)建 store 開(kāi)始吧。

創(chuàng)建 Object Store

使用的方法就是 IDBDatabase 上的 createObjectStore 方法。

var objectStore = db.createObjectStore("customers", { keyPath: "id",{autoIncrement:true} });

基本函數(shù)構(gòu)造為:

IDBObjectStore createObjectStore(DOMString name,
                                               optional IDBObjectStoreParameters options)
                                               
dictionary IDBObjectStoreParameters {
  (DOMString or sequence)? keyPath = null;
  boolean autoIncrement = false;
};

keyPath: 用來(lái)設(shè)置主鍵的 key,具體區(qū)別可以參考下面的 keyPath 和 generator 的區(qū)別。

autoIncrement: 是否使用自增 key 的特性。

創(chuàng)建的 key 主要是為了保證,在數(shù)據(jù)插入時(shí)唯一性的標(biāo)識(shí)。

不過(guò),往往一個(gè)主鍵(key),是沒(méi)辦法很好的完成索引,在具體實(shí)踐時(shí),就還需要輔鍵 (aid-key) 來(lái)完成輔助索引工作,這個(gè)在 IndexDB 就映射為 index。

設(shè)置索引 index

在完成 PK(Primary key) 創(chuàng)建完畢后,為了更好的搜索性能我們還需要額外創(chuàng)建 index。這里可以直接使用:

objectStore.createIndex("indexName", "property", options);

indexName: 設(shè)置當(dāng)前 index 的名字

property: 從存儲(chǔ)數(shù)據(jù)中,指明 index 所指的屬性。

其中,options 有三個(gè)選項(xiàng):

unique: 當(dāng)前 key 是否能重復(fù) (最常用)

multiEntry: 設(shè)置當(dāng)前的 property 為數(shù)組時(shí),會(huì)給數(shù)組里面每個(gè)元素都設(shè)置一個(gè) index 值。

# 創(chuàng)建一個(gè)名字叫 titleIndex 的 index,并且存儲(chǔ)的 index 不能重復(fù)
DB.createIndex("titleIndex", "title", {unique: false});

具體可以參考:MDN createIndex Prop 和 googleDeveloper Index。

增刪數(shù)據(jù)

在 IndexedDB 里面進(jìn)行數(shù)據(jù)的增刪,都需要在 transaction 中完成。而這個(gè)增刪數(shù)據(jù),大家可以理解為一次 request,相當(dāng)于在一個(gè) transaction 里面管理所有當(dāng)前邏輯操作的 request。所以,在正式開(kāi)始進(jìn)行數(shù)據(jù)操作之前,還需要給大家簡(jiǎn)單介紹一些如果創(chuàng)建一個(gè)事務(wù)。

事務(wù)的創(chuàng)建

transaction API,如下 [代碼1]。在創(chuàng)建時(shí),你需要手動(dòng)指定當(dāng)前 transaction 是那種類(lèi)型的操作,基本的內(nèi)容有:

"readonly":只讀

"readwrite":讀寫(xiě)

"versionchange":這個(gè)不能手動(dòng)指定,會(huì)在 upgradeneeded 回調(diào)事件里面自動(dòng)創(chuàng)建。它可以用來(lái)修改現(xiàn)有 object store 的結(jié)構(gòu)數(shù)據(jù),比如 index 等。

你可以通過(guò)在數(shù)據(jù)庫(kù)打開(kāi)之后,通過(guò) IDBDataBase 上的 transaction 方法創(chuàng)建,如 [代碼2]。

[代碼1]
  [NewObject] IDBTransaction transaction((DOMString or sequence) storeNames,
                                         optional IDBTransactionMode mode = "readonly");
                                         
[代碼2]
var transaction = db.transaction(["customers"], "readwrite");
var objectStore = transaction.objectStore("customers");
# 遍歷存儲(chǔ)數(shù)據(jù)
for (var i in customerData) {
  var request = objectStore.add(customerData[i]);
  request.onsuccess = function(event) {
    // success, done?
  };
}

事務(wù)在創(chuàng)建的時(shí)候不僅僅可以制定執(zhí)行的模式,還可以指定本次事務(wù)能夠影響的 ObjectStore 范圍,具體細(xì)節(jié)就是在第一個(gè) transaction 參數(shù)里面?zhèn)魅氲氖且粋€(gè)數(shù)據(jù),然后通過(guò) objectStore() 方法打開(kāi)多個(gè) OS 進(jìn)行操作,如下 [代碼3]。

[代碼3]
var tx = db.transaction(["books","person"], "readonly");
var books = tx.objectStore("books");
var person = tx.objectStore("person");
操作數(shù)據(jù)

完成了事務(wù)的創(chuàng)建之后,我們就可以正式的開(kāi)始進(jìn)行數(shù)據(jù)的交互操作了,也就是寫(xiě)我們具體的業(yè)務(wù)邏輯。如下 [代碼1],一個(gè)完整數(shù)據(jù)事務(wù)的操作。

[代碼1]
var tx = db.transaction("books", "readwrite");
var store = tx.objectStore("books");

store.put({title: "Quarry Memories", author: "Fred", isbn: 123456});
store.put({title: "Water Buffaloes", author: "Fred", isbn: 234567});
store.put({title: "Bedrock Nights", author: "Barney", isbn: 345678});

tx.oncomplete = function() {
  // All requests have succeeded and the transaction has committed.
};

通過(guò) objectStore 回調(diào)得到的 IDBObjectStore 對(duì)象,我們就可以進(jìn)行一些列的增刪查改操作了。可以參考 [代碼2]。詳細(xì)的可以參考文末的 appendix。

[代碼2]
  [NewObject] IDBRequest put(any value, optional any key);
  [NewObject] IDBRequest add(any value, optional any key);
  [NewObject] IDBRequest delete(any query);
索引數(shù)據(jù)

索引數(shù)據(jù)是所有數(shù)據(jù)庫(kù)里面最重要的一個(gè)。這里,我們可以使用游標(biāo),index 來(lái)做。例如,通過(guò) index 來(lái)快速索引 key 值,參考 [代碼1]。

[代碼1]
var index = objectStore.index("name");
index.get("Donna").onsuccess = function(event) {
  alert("Donna"s SSN is " + event.target.result.ssn);
};

更詳細(xì)的內(nèi)容,可以參考下文 數(shù)據(jù)索引方式。

keyPath 和 key Generator

何謂 keyPath 和 keyGenerator 應(yīng)該算是 IndexedDB 里面比較難以理解的概念。簡(jiǎn)單來(lái)說(shuō),IndexedDB 在創(chuàng)建 Store 的時(shí)候,必須保證里面的數(shù)據(jù)是唯一的,那么得需要像其它數(shù)據(jù)庫(kù)一樣設(shè)置一個(gè) primary Key 來(lái)區(qū)分不同數(shù)據(jù)。而 keyPath 和 Generator 就是兩種不同的設(shè)置 key 的方式。

設(shè)置 keyPath

# 設(shè)置預(yù)先需要存放的數(shù)據(jù)

const customerData = [
  { ssn: "444-44-4444", name: "Bill", age: 35, email: "bill@company.com" },
  { ssn: "555-55-5555", name: "Donna", age: 32, email: "donna@home.org" }
];

# 通過(guò) keyPath 設(shè)置 Primary Key
var objectStore = db.createObjectStore("customers", { keyPath: "ssn" });

因?yàn)?ssn 在該數(shù)據(jù)集是唯一的,所以,我們可以利用它來(lái)作為 keyPath 保證 unique 的特性?;蛘?,可以設(shè)置為自增的鍵值,比如 id++ 類(lèi)似的。

upgradeDb.createObjectStore("logs", {keyPath: "id", autoIncrement:true});

使用 generator

generator 會(huì)每次在添加數(shù)據(jù)時(shí),自動(dòng)創(chuàng)建一個(gè) unique value。這個(gè) unique value 是和你的實(shí)際數(shù)據(jù)是分開(kāi)的。里面直接通過(guò) autoIncrement:true 來(lái)設(shè)置即可。

upgradeDb.createObjectStore("notes", {autoIncrement:true});
indexDB 打開(kāi)注意事項(xiàng)

檢查是否支持 indexDB

if (!("indexedDB" in window)) {
  console.log("This browser doesn"t support IndexedDB");
  return;
}

版本更新: indexDB

在生成一個(gè) indexDB 實(shí)例時(shí),需要手動(dòng)指定一個(gè)版本號(hào)。而最常用的

idb.open("test-db7", 2, function(upgradeDb) {})

這樣會(huì)造成一個(gè)問(wèn)題,比如上線過(guò)程中,用戶A第一次請(qǐng)求返回了新版本的網(wǎng)頁(yè),連接了版本2。之后又刷新網(wǎng)頁(yè)命中了另一臺(tái)未上線的機(jī)器,連接了舊版本1 出錯(cuò)。主要原因是:

indexedDB API 中不允許數(shù)據(jù)庫(kù)中的數(shù)據(jù)倉(cāng)庫(kù)在同一版本中發(fā)生變化. 并且當(dāng)前 DB 版本不能和低版本的 version 連接。

比如,你一開(kāi)始定義的 DB 版本內(nèi)容為:

# 版本一定義的內(nèi)容
db.version(1).stores({friends: "++id,name"});

# 版本二修改結(jié)構(gòu)為:
db.version(2).stores({friends: "++id,name,shoeSize"});

如果此時(shí),用戶先打開(kāi)了 version(1),但是后面,又得到的是 version(2) 版本的 HTML,這時(shí)就會(huì)出現(xiàn) error 的錯(cuò)誤。

參考:

版本更替

版本更新

這個(gè)在 IndexDB 是一個(gè)很重要的問(wèn)題。主要原因在于

indexedDB API 中不允許數(shù)據(jù)庫(kù)中的數(shù)據(jù)倉(cāng)庫(kù)在同一版本中發(fā)生變化. 并且當(dāng)前 DB 版本不能和低版本的 version 連接。

上面就可以抽象為一個(gè)問(wèn)題:

你什么情況下需要更新 IndexDB 的版本呢?

該表數(shù)據(jù)庫(kù)里面的 keyPath 時(shí)。

你需要重新設(shè)計(jì)數(shù)據(jù)庫(kù)表結(jié)構(gòu)時(shí),比如新增 index

# 版本 1 的 DB 設(shè)計(jì),有一個(gè)主鍵 id 和 index-name
db
.version(1)
.stores({friends: "++id,name"})

# 如果直接想新增一個(gè) key,例如 male,是無(wú)法成功的
db
.version(1)
.stores({friends: "++id,name,male"})

# 正確辦法是直接修改版本號(hào)更新
db
.version(2)
.stores({friends: "++id,name,male"})

不過(guò),如果直接修改版本號(hào),會(huì)出現(xiàn)這樣一個(gè) case:

由于原始 HTML 更新問(wèn)題,用戶首先訪問(wèn)的是版本 1 的 A 頁(yè)面,然后,訪問(wèn)更新過(guò)后的 B 頁(yè)面。這時(shí),IndexDB 成功更新為高版本。但是,用戶下次又命中了老版本的 A 頁(yè)面,此時(shí) A 中還是連接低版本的 IndexDB ,就會(huì)報(bào)錯(cuò),導(dǎo)致你訪問(wèn)失敗。

解決辦法就是,設(shè)置過(guò)濾,在 open 的時(shí)候,手動(dòng)傳入版本號(hào):

# 打開(kāi)版本 1 的數(shù)據(jù)庫(kù)
var dbPromise = idb.open("db1", 1, function(upgradeDb){...})

# 打開(kāi)版本 2 的數(shù)據(jù)庫(kù)
var dbPromise = idb.open("db2", 2, function(upgradeDb){...})

不過(guò),這樣又會(huì)造成另外一個(gè)問(wèn)題,即,數(shù)據(jù)遷移(老版本數(shù)據(jù),不可能不要吧)。這里,IndexDB 會(huì)有一個(gè) updateCallback 給你觸發(fā),你可以直接在里面做相關(guān)的數(shù)據(jù)遷移處理。

var dbPromise = idb.open("test-db7", 2, function(upgradeDb) {
  switch (upgradeDb.oldVersion) {
    case 0:
      upgradeDb.createObjectStore("store", {keyPath: "name"});
    case 1:
      var peopleStore = upgradeDb.transaction.objectStore("store");
      peopleStore.createIndex("price", "price");
  }
});

在使用的時(shí)候,一定要注意 DB 版本的升級(jí)處理,比如有這樣一個(gè) case,你的版本已經(jīng)是 3,不過(guò),你需要處理版本二的數(shù)據(jù):

# 將版本二 中的 name 拆分為  firstName 和 lastName
db.version(3).stores({friends: "++id,shoeSize,firstName,lastName"}).upgrade(function(t) {
    
    return t.friends.toCollection().modify(function(friend) {
        // Modify each friend:
        friend.firstName = friend.name.split(" ")[0];
        friend.lastName = friend.name.split(" ")[1];
        delete friend.name;
    });
});

對(duì)于存在版本 2 數(shù)據(jù)庫(kù)的用戶來(lái)說(shuō)是 OK 的,但是對(duì)于某些還沒(méi)有訪問(wèn)過(guò)你數(shù)據(jù)庫(kù)的用戶來(lái)說(shuō),這無(wú)疑就報(bào)錯(cuò)了。解決辦法有:

保留每個(gè)版本時(shí),創(chuàng)建的字段和 stores

在更新 callback 里面,對(duì)處理的數(shù)據(jù)判斷是否存在即可。

在 Dexie.js DB 數(shù)據(jù)庫(kù)中,需要你保留每次 DB 創(chuàng)建的方法,實(shí)際上是通過(guò) 添加 swtich case ,來(lái)完成每個(gè)版本的更新:

# Dexie.js 保留 DB 數(shù)據(jù)庫(kù)
db.version(1).stores({friends: "++id,name"});
db.version(2).stores({friends: "++id,name,shoeSize"});
db.version(3).stores({friends: "++id,shoeSize,firstName,lastName"}).upgrade(...)

# 內(nèi)部原理,直接添加 switch case 完成版本更新
var dbPromise = idb.open("test-db7", 2, function(upgradeDb) {
  switch (upgradeDb.oldVersion) {
    case 0:
      upgradeDb.createObjectStore("store", {keyPath: "name"});
    case 1:
      var peopleStore = upgradeDb.transaction.objectStore("store");
      peopleStore.createIndex("price", "price");
  }
});

如果遇到一個(gè)頁(yè)面打開(kāi),但是另外一個(gè)頁(yè)面拉取到新的代碼進(jìn)行更新時(shí),這個(gè)時(shí)候還需要將低版本 indexedDB 進(jìn)行顯式的關(guān)閉。具體操作辦法就是監(jiān)聽(tīng) onversionchange 事件,當(dāng)版本升級(jí)時(shí),通知當(dāng)前 DB 進(jìn)行關(guān)閉,然后在新的頁(yè)面進(jìn)行更新操作。

openReq.onupgradeneeded = function(event) {
  // 所有其它數(shù)據(jù)庫(kù)都已經(jīng)被關(guān)掉了,直接更新代碼
  db.createObjectStore(/* ... */);
  db.onversionchange = function(event) {
    db.close();
  };

}  
  

最后,更新是還有幾個(gè)注意事項(xiàng):

版本更新不能改變 primary key

回退代碼時(shí),千萬(wàn)注意版本是否已經(jīng)更新。否則,只能增量更新,重新修改版本號(hào)來(lái)修復(fù)。

存儲(chǔ)加密特性

有時(shí)候,我們存儲(chǔ)時(shí),想得到一個(gè)由一串 String 生成的 hash key,那在 Web 上應(yīng)該如何實(shí)現(xiàn)呢?

這里可以直接利用 Web 上已經(jīng)實(shí)現(xiàn)的 WebCrypto,為了實(shí)現(xiàn)上述需求,我們可以直接利用里面的 digest 方法即可。這里 MDN 上,已經(jīng)有現(xiàn)成的辦法,我們直接使用即可。

參考:

WebCrypto 加密手段

存儲(chǔ)上限值

基本限制為:

瀏覽器 限制
Chrome 可用空間 < 6%
Firebox 可用空間 < 10%
Safari < 50MB
IE10 < 250MB

逐出策略為:

瀏覽器 逐出政策
Chrome 在 Chrome 耗盡空間后采用 LRU 策略
Firebox 在整個(gè)磁盤(pán)已裝滿時(shí)采用 LRU 策略
Safari 無(wú)逐出
Edge 無(wú)逐出

參考:

存儲(chǔ)上限值
瀏覽器內(nèi)核存儲(chǔ)上限值處理

數(shù)據(jù)索引方式

在數(shù)據(jù)庫(kù)中除了基本的 CRUD 外,一個(gè)高效的索引架構(gòu),則是里面的重中之重。在 indexedDB 中,我們一共可以通過(guò)三種方式來(lái)索引數(shù)據(jù):

固定的 key 值

索引外鍵(index)

游標(biāo)(cursor)

固定 key 索引

IDBObjectStore 提供給了我們直接通過(guò) primaryKey 來(lái)索引數(shù)據(jù),參考 [代碼1],這種方式需要我們一開(kāi)始就知道目標(biāo)的 key 內(nèi)容。當(dāng)然,也可以通過(guò) getAll 全部索引數(shù)據(jù)。

[代碼1]
  [NewObject] IDBRequest get(any query);
  [NewObject] IDBRequest getKey(any query);
  [NewObject] IDBRequest getAll(optional any query,
                                optional [EnforceRange] unsigned long count);
  [NewObject] IDBRequest getAllKeys(optional any query,
                                    optional [EnforceRange] unsigned long count);

比如,我們通過(guò) primaryKey 得到一條具體的數(shù)據(jù):

db.transaction("customers").objectStore("customers").get("id_card_1118899").onsuccess = function(event) {
    // data is event.target.result.name
};

也可以 fetch 整個(gè) Object Store 的數(shù)據(jù)。這些場(chǎng)景用處比較少,這里就不過(guò)多講解。我們主要來(lái)了解一下 index 的索引方式。

index 索引

如果想要查詢某個(gè)數(shù)據(jù),直接通過(guò)整個(gè)對(duì)象來(lái)進(jìn)行遍歷的話,這樣做性能耗時(shí)是非常大的。如果我們結(jié)合 index 來(lái)將 key 加以分類(lèi),就可以很快速的實(shí)現(xiàn)指定數(shù)據(jù)的索引。這里,我們可以直接利用 IDBObjectStore 上面的 index() 方法來(lái)獲取指定 index 的值,具體方法可以參考 [代碼1]。

[代碼1]
 IDBIndex index(DOMString name);

該方法會(huì)直接返回一個(gè) IDBIndex 對(duì)象。這你也可以理解為一個(gè)類(lèi)似 ObjectStore 的微型 index 數(shù)據(jù)內(nèi)容。接著,我們可以使用 get() 方法來(lái)獲得指定 index 的數(shù)據(jù),參考[代碼2]。

[代碼2]
var index = objectStore.index("name");
index.get("Donna").onsuccess = function(event) {
  alert("Donna"s SSN is " + event.target.result.ssn);
};

使用 get 方法不管你的 index 是否是 unique 的都會(huì)只會(huì)返回第一個(gè)數(shù)據(jù)。如果想得到多個(gè)數(shù)據(jù)的話,可以使用 getAll(key) 來(lái)做。通過(guò) getAll() 得到的回調(diào)函數(shù),直接通過(guò) event.target.result 可以得到對(duì)應(yīng)的 value 內(nèi)容。

objectStore.getAll().onsuccess = function(event) {
      printf(event.target.result); // Array
    };

除了通過(guò) getAll() 得到所有數(shù)據(jù)外,還可以采用更高效的 cursor 方法遍歷得到的數(shù)據(jù)。

參考:

getAll() 和 openCursor 實(shí)例

游標(biāo)索引

所謂的游標(biāo),大家心里應(yīng)該可以有一個(gè)初步的印象,就像我們物理尺子上的那個(gè)東西,可以自由的移動(dòng),來(lái)標(biāo)識(shí)指向的對(duì)象內(nèi)容。cursor 里面有兩個(gè)核心的方法:

advance(count): 將當(dāng)前游標(biāo)位置向前移動(dòng) count 位置

continue(key): 將當(dāng)前游標(biāo)位置移動(dòng)到指定 key 的位置,如果沒(méi)提供 key 則代表的移動(dòng)下一個(gè)位置。

比如,我們使用 cursor 來(lái)遍歷 Object Store 的具體數(shù)據(jù)。

objectStore.openCursor().onsuccess = function(event) {
    var cursor = event.target.result;
    if(cursor) {
        // cursor.key 
        // cursor.value
      cursor.continue();
    } else {
      console.log("Entries all displayed.");
    }
  };

通常,游標(biāo)可以用來(lái)遍歷兩個(gè)類(lèi)型的數(shù)據(jù),一個(gè)是 ObjectStore、一個(gè)是 Index。

Object.store: 如果在該對(duì)象上使用游標(biāo),那么會(huì)根據(jù) primaryKey 遍歷整個(gè)數(shù)據(jù),注意,這里不會(huì)存在重復(fù)的情況,因?yàn)?primaryKey 是唯一的。

index: 在 index 上使用游標(biāo)的話,會(huì)以當(dāng)前的 index 來(lái)進(jìn)行遍歷,其中可能會(huì)存在重復(fù)的現(xiàn)象。

在 IDBObjectStore 對(duì)象上有兩種方法來(lái)打開(kāi)游標(biāo):

openCursor: 遍歷的對(duì)象是 具體的數(shù)據(jù)值,最常用的方法

openKeyCursor: 遍歷的對(duì)象是 數(shù)據(jù) key 值

這里,我們通過(guò) openCursor 來(lái)直接打開(kāi)一個(gè) index 數(shù)據(jù)集,然后進(jìn)行遍歷。

PersonIndex.openCursor().onsuccess = function(event) {
  var cursor = event.target.result;
  if (cursor) {
    customers.push(cursor.value);
    cursor.continue();
  }
  else {
    alert("Got all customers: " + customers);
  }
};

在游標(biāo)中,還提供給了一個(gè) updatedelete 方法,我們可以用它來(lái)進(jìn)行數(shù)據(jù)的更新操作,否則的話就直接使用 ObjectStore 提供的 put 方法。

游標(biāo)里面我們還可以限定其遍歷的范圍和方向。這個(gè)設(shè)置是我們直接在 openCursor() 方法里面?zhèn)鲄⑼瓿傻?,該方法的?gòu)造函數(shù)參考 [代碼1]。他里面可以傳入兩個(gè)參數(shù),第一個(gè)用來(lái)指定范圍,第二個(gè)用來(lái)指定 cursor 移動(dòng)的方向。

[代碼1]
IDBRequest openCursor(optional any query,
                                    optional IDBCursorDirection direction = "next");

如果需要對(duì) cursor 設(shè)置范圍的話,就需要使用到 IDBKeyRange 這個(gè)對(duì)象,使用樣板可以參考 [代碼2]。IDBKeyRange 里面 key 參考的對(duì)象 因使用者的不同而不同。如果是針對(duì) ObjectStore 的話,則是針對(duì) primaryKey,如果是針對(duì) Index 的話,則是針對(duì)當(dāng)前的 indexKey

/ 匹配所有在 “Bill” 前面的, 但是不需要包括 "Bill"
var lowerBoundOpenKeyRange = IDBKeyRange.lowerBound("Bill", true);

比如,我們這里對(duì) PersonIndex 設(shè)置一個(gè) index 范圍,即,索引 在 villainhrjimmyVV 之間的數(shù)據(jù)集合。

# 都包括 villainhr 和 jimmyVV 的數(shù)據(jù)
var boundKeyRange = IDBKeyRange.bound("villainhr", "jimmyVV", true, true);

 PersonIndex.openCursor(boundKeyRange).onsuccess = function(event) {
  var cursor = event.target.result;
  if (cursor) {
    // Do something with the matches.
    cursor.continue();
  }
};

如果你還想設(shè)置遍歷的方向和是否排除重復(fù)數(shù)據(jù),還可以根據(jù) [代碼2] 的枚舉類(lèi)型來(lái)設(shè)置。比如,在 [代碼3] 中,我們改變默認(rèn)的 cursor 遍歷數(shù)據(jù)的方向?yàn)?prev,從末尾開(kāi)始。

[代碼2]
enum IDBCursorDirection {
  "next",
  "nextunique",
  "prev",
  "prevunique"
};

[代碼3]
objectStore.openCursor(null, IDBCursor.prev).onsuccess = function(event) {
  var cursor = event.target.result;
  if (cursor) {
    // cursor.value 
    cursor.continue();
  }
};
事務(wù)讀取性能

在 indexDB 里面的讀寫(xiě)全部是基于 transaction 模式來(lái)的。也就是 IDBDataBase 里面的 transaction 方法,如下 [代碼1]。所有的讀寫(xiě)都可以比作在 transaction 作用域下的請(qǐng)求,只有當(dāng)所有請(qǐng)求完成之后,該次 transaction 才會(huì)生效,否則就會(huì)拋出異常或者錯(cuò)誤。transaction 會(huì)根據(jù)監(jiān)聽(tīng) error,abort,以及 complete 三個(gè)事件來(lái)完成整個(gè)事務(wù)的流程管理,參考[代碼2]。

[代碼1]
  [NewObject] IDBTransaction transaction((DOMString or sequence) storeNames,
                                         optional IDBTransactionMode mode = "readonly");

[代碼2]
  attribute EventHandler onabort;
  attribute EventHandler oncomplete;
  attribute EventHandler onerror;

例如:

var request = db.transaction(["customers"], "readwrite")
                .objectStore("customers")
                .delete("gg");
request.onsuccess = function(event) {
  // delete, done
};

你可以在 transaction 方法里面手動(dòng)傳入 readwrite 或者其他表示事務(wù)的 readonly 參數(shù),來(lái)表示本次事務(wù)你會(huì)進(jìn)行如何的操作。IndexedDB 在初始設(shè)計(jì)時(shí),就已經(jīng)決定了它的性能問(wèn)題。

只含有 readonly 模式的 transaction 可以并發(fā)進(jìn)行執(zhí)行
含有 write 模式的 transaction 必須按照隊(duì)列 來(lái) 執(zhí)行

這就意味著,如果你使用了 readwrite 模式的話,那么后續(xù)不管是不是 readonly 都必須等待該次 transaction 完成才行。

常用技巧 生成 id++ 的主鍵

指定 primaryKey 生成時(shí),是通過(guò) createObjectStore 方法來(lái)操作的。有時(shí)候,我們會(huì)遇到想直接得到一個(gè) key,并且存在于當(dāng)前數(shù)據(jù)集中,可以在 options 中同時(shí)加上 keyPathautoIncrement 屬性。該 key 的范圍是 [1- $ 2^{53} $],參考 keygenerator key 的大小

db.createObjectStore("table1", {keyPath: "id", autoIncrement: true});
推薦

閱讀推薦

indexedDB W3C 文檔
indexedDB 入門(mén)
MDN indexedDB 入門(mén)

好用庫(kù)推薦

idb: 一個(gè) promise 的 DB 庫(kù)

Indexed Appendix

IndexedDB 數(shù)據(jù)庫(kù)使用key-value鍵值對(duì)儲(chǔ)存數(shù)據(jù).你可以對(duì)對(duì)象的某個(gè)屬性創(chuàng)建索引(index)以實(shí)現(xiàn)快速查詢和列舉排序。.key可以使二進(jìn)制對(duì)象

IndexedDB 是事務(wù)模式的數(shù)據(jù)庫(kù). IndexedDB API提供了索引(indexes), 表(tables), 指針(cursors)等等, 但是所有這些必須是依賴于某種事務(wù)的。

The IndexedDB API 基本上是異步的.

IndexedDB 數(shù)據(jù)庫(kù)的請(qǐng)求都會(huì)包含 onsuccess和onerror事件屬性。

IndexedDB 在結(jié)果準(zhǔn)備好之后通過(guò)DOM事件通知用戶

IndexedDB是面向?qū)ο蟮?。indexedDB不是用二維表來(lái)表示集合的關(guān)系型數(shù)據(jù)庫(kù)。這一點(diǎn)非常重要,將影響你設(shè)計(jì)和建立你的應(yīng)用程序。

indexedDB不使用結(jié)構(gòu)化查詢語(yǔ)言(SQL)。它通過(guò)索引(index)所產(chǎn)生的指針(cursor)來(lái)完成查詢操作,從而使你可以迭代遍歷到結(jié)果集合。

IndexedDB遵循同源(same-origin)策略

局限和移除 case

全球多種語(yǔ)言混合存儲(chǔ)。國(guó)際化支持不好。需要自己處理。

和服務(wù)器端數(shù)據(jù)庫(kù)同步。你得自己寫(xiě)同步代碼。

全文搜索。

在以下情況下,數(shù)據(jù)庫(kù)可能被清除:

用戶請(qǐng)求清除數(shù)據(jù)。

瀏覽器處于隱私模式。最后退出瀏覽器的時(shí)候,數(shù)據(jù)會(huì)被清除。

硬盤(pán)等存儲(chǔ)設(shè)備的容量到限。

不正確的

不完整的改變.

常規(guī)概念

數(shù)據(jù)庫(kù)

數(shù)據(jù)庫(kù): 通常包含一個(gè)或多個(gè) object stores. 每個(gè)數(shù)據(jù)庫(kù)必須包含以下內(nèi)容:

名字(Name): 它標(biāo)識(shí)了一個(gè)特定源中的數(shù)據(jù)庫(kù),并且在數(shù)據(jù)庫(kù)的整個(gè)生命周期內(nèi)保持不變. 此名字可以為任意字符串值(包括空字符串).

當(dāng)前版本(version). 當(dāng)一個(gè)數(shù)據(jù)庫(kù)首次創(chuàng)建時(shí),它的 version 為1,除非另外指定. 每個(gè)數(shù)據(jù)庫(kù)在任意時(shí)刻只能有一個(gè) version

對(duì)象存儲(chǔ)(object store): 用來(lái)承載數(shù)據(jù)的一個(gè)分區(qū).數(shù)據(jù)以鍵值對(duì)形式被對(duì)象存儲(chǔ)永久持有。在 OS 中,創(chuàng)建一個(gè) key 可以使用 key generatorkey path。

key generator: 簡(jiǎn)單來(lái)說(shuō)就是在存儲(chǔ)數(shù)據(jù)時(shí),主動(dòng)生成一個(gè) id++ 來(lái)區(qū)分每條記錄。這種情況下 存儲(chǔ)數(shù)據(jù)的 key 是和 value 分開(kāi)進(jìn)行存儲(chǔ)的,也就是 (out of line)。

key path: 需要用戶主動(dòng)來(lái)設(shè)置儲(chǔ)存數(shù)據(jù)的 key 內(nèi)容,

request: 每次讀寫(xiě)操作,可以當(dāng)做一次 request.

transaction: 一系列讀寫(xiě)請(qǐng)求的集合。

index: 一個(gè)特殊的 Object Store,用來(lái)索引另外一個(gè) Store 的數(shù)據(jù)。

具體數(shù)據(jù) key/value

key: 這個(gè) key 的值,可以通過(guò)三種方式生成。 a key generator, a key path, 用戶指定的值。并且,這個(gè) key 在當(dāng)前的 Object Store 是唯一的。一個(gè) key 類(lèi)型可以是 string, date, float, and array 類(lèi)型。不過(guò),在老版本的時(shí)候,一般只支持 string or integer。(現(xiàn)在,版本應(yīng)該都 OK 了)

- key generator: 相當(dāng)于以一種 `id++` 的形式來(lái)生成一個(gè) key 值。
- key path: 當(dāng)前指定的 key 可以根據(jù) value 里面的內(nèi)容來(lái)指定。里面可以為一些分隔符。
- 指定的 key:這個(gè)就是需要用戶手動(dòng)來(lái)指定生成。

value: 可以存儲(chǔ) boolean, number, string, date, object, array, regexp, undefined, and null。現(xiàn)在還可以存儲(chǔ) files and blob 對(duì)象。

操作作用域

scope:這可以比作 transaction 的作用域,即,一系列 transaction 執(zhí)行的順序。該規(guī)定,多個(gè) reading transaction 能夠同時(shí)執(zhí)行。但是 writing 則只能排隊(duì)進(jìn)行。

key range: 用來(lái)設(shè)置取出數(shù)據(jù)的 key 的范圍內(nèi)容。

參考:

原生概念 IndexedDB

IDBFactory

這其實(shí)就是 indexDB 上面掛載的對(duì)象。主要 API 如下:

[Exposed=(Window,Worker)]
interface IDBFactory {
  [NewObject] IDBOpenDBRequest open(DOMString name,
                                    optional [EnforceRange] unsigned long long version);
  [NewObject] IDBOpenDBRequest deleteDatabase(DOMString name);

  short cmp(any first, any second);
};

你可以直接通過(guò) open 來(lái)打開(kāi)一個(gè)數(shù)據(jù)庫(kù)。通過(guò) 返回一個(gè) Request 對(duì)象,來(lái)進(jìn)行結(jié)果監(jiān)聽(tīng)的回調(diào):

var request = indexedDB.open("AddressBook", 15);
request.onsuccess = function(evt) {...};
request.onerror = function(evt) {...};

參考:

IndexDB Factory API

IDBRequest

當(dāng)你通過(guò) open 方法處理過(guò)后,就會(huì)得到一個(gè) Request 回調(diào)對(duì)象。這個(gè)就是 IDBRequest 的實(shí)例。

[Exposed=(Window,Worker)]
interface IDBRequest : EventTarget {
  readonly attribute any result; // 通過(guò) open 打開(kāi)過(guò)后的 IDBObjectStore 實(shí)例 
  readonly attribute DOMException? error;
  readonly attribute (IDBObjectStore or IDBIndex or IDBCursor)? source;
  readonly attribute IDBTransaction? transaction;
  readonly attribute IDBRequestReadyState readyState;

  // Event handlers:
  attribute EventHandler onsuccess;
  attribute EventHandler onerror;
};

enum IDBRequestReadyState {
  "pending",
  "done"
};

[Exposed=(Window,Worker)]
interface IDBOpenDBRequest : IDBRequest {
  // Event handlers:
  attribute EventHandler onblocked;
  attribute EventHandler onupgradeneeded;
};

你可以通過(guò) result 得到當(dāng)前數(shù)據(jù)庫(kù)操作的結(jié)果。如果你打開(kāi)更新后的版本號(hào)的話,還需要監(jiān)聽(tīng) onupgradeneeded 事件來(lái)實(shí)現(xiàn)。最常通過(guò) indexedDB.open 遇見(jiàn)的錯(cuò)誤就是 VER_ERR 版本錯(cuò)誤。這表明存儲(chǔ)在磁盤(pán)上的數(shù)據(jù)庫(kù)的版本高于你試圖打開(kāi)的版本。

db.onerror = function(event) {
  // Generic error handler for all errors targeted at this database"s
  // requests!
  alert("Database error: " + event.target.errorCode);
};

所以,一般在創(chuàng)建 IndexDB 時(shí),還需要管理它版本的更新操作,這里就需要監(jiān)聽(tīng) onupgradeneeded 來(lái)是實(shí)現(xiàn)。

request.onupgradeneeded = function(event) { 
   // 更新對(duì)象存儲(chǔ)空間和索引 .... 
};

或者我們可以直接使用 idb 微型庫(kù)來(lái)實(shí)現(xiàn)讀取操作。

  var dbPromise = idb.open("test-db3", 1, function(upgradeDb) {
    if (!upgradeDb.objectStoreNames.contains("people")) {
      upgradeDb.createObjectStore("people", {keyPath: "email"});
    }
    if (!upgradeDb.objectStoreNames.contains("notes")) {
      upgradeDb.createObjectStore("notes", {autoIncrement: true});
    }
    if (!upgradeDb.objectStoreNames.contains("logs")) {
      upgradeDb.createObjectStore("logs", {keyPath: "id", autoIncrement: true});
    }
  });

其中通過(guò) onupgradeneeded 回調(diào)得到的 event.result 就是 IDBDatabase 的實(shí)例,常常用來(lái)設(shè)置 index 和插入數(shù)據(jù)。參考下面內(nèi)容。

參考:

IDBRequest API

IDBDatabase

該對(duì)象常常用來(lái)做 Object Store 和 transaction 的創(chuàng)建和刪除。該部分是 onupgradeneeded 事件獲得的 event.target.result 對(duì)象:

request.onupgradeneeded = function(event) { 
   // 更新對(duì)象存儲(chǔ)空間和索引 .... 
   // event.target.result 對(duì)象
};

具體 API 內(nèi)容如下:

[Exposed=(Window,Worker)]
interface IDBDatabase : EventTarget {
  readonly attribute DOMString name;
  readonly attribute unsigned long long version;
  readonly attribute DOMStringList objectStoreNames;

  [NewObject] IDBTransaction transaction((DOMString or sequence) storeNames,
                                         optional IDBTransactionMode mode = "readonly");
  void close();

  [NewObject] IDBObjectStore createObjectStore(DOMString name,
                                               optional IDBObjectStoreParameters options);
  void deleteObjectStore(DOMString name);

  // Event handlers:
  attribute EventHandler onabort;
  attribute EventHandler onclose;
  attribute EventHandler onerror;
  attribute EventHandler onversionchange;
};

dictionary IDBObjectStoreParameters {
  (DOMString or sequence)? keyPath = null;
  boolean autoIncrement = false;
};

如果它通過(guò) createObjectStore 方法,那么得到的就是一個(gè) IDBObjectStore 實(shí)例對(duì)象。如果是 transaction 方法,那么就是 IDBTransaction 對(duì)象。

IDBObjectStore

該對(duì)象一般是用來(lái)創(chuàng)建 index 和插入數(shù)據(jù)使用。

可以參考:

[Exposed=(Window,Worker)]
interface IDBObjectStore {
  attribute DOMString name;
  readonly attribute any keyPath;
  readonly attribute DOMStringList indexNames;
  [SameObject] readonly attribute IDBTransaction transaction;
  readonly attribute boolean autoIncrement;

  [NewObject] IDBRequest put(any value, optional any key);
  [NewObject] IDBRequest add(any value, optional any key);
  [NewObject] IDBRequest delete(any query);
  [NewObject] IDBRequest clear();
  [NewObject] IDBRequest get(any query);
  [NewObject] IDBRequest getKey(any query);
  [NewObject] IDBRequest getAll(optional any query,
                                optional [EnforceRange] unsigned long count);
  [NewObject] IDBRequest getAllKeys(optional any query,
                                    optional [EnforceRange] unsigned long count);
  [NewObject] IDBRequest count(optional any query);

  [NewObject] IDBRequest openCursor(optional any query,
                                    optional IDBCursorDirection direction = "next");
  [NewObject] IDBRequest openKeyCursor(optional any query,
                                       optional IDBCursorDirection direction = "next");

  IDBIndex index(DOMString name);

  [NewObject] IDBIndex createIndex(DOMString name,
                                   (DOMString or sequence) keyPath,
                                   optional IDBIndexParameters options);
  void deleteIndex(DOMString name);
};

dictionary IDBIndexParameters {
  boolean unique = false;
  boolean multiEntry = false;
};
IDBIndex

該對(duì)象是用來(lái)進(jìn)行 Index 索引的操作對(duì)象,里面也會(huì)存在 getgetAll 等方法。詳細(xì)內(nèi)容如下:

[Exposed=(Window,Worker)]
interface IDBIndex {
  attribute DOMString name;
  [SameObject] readonly attribute IDBObjectStore objectStore;
  readonly attribute any keyPath;
  readonly attribute boolean multiEntry;
  readonly attribute boolean unique;

  [NewObject] IDBRequest get(any query);
  [NewObject] IDBRequest getKey(any query);
  [NewObject] IDBRequest getAll(optional any query,
                                optional [EnforceRange] unsigned long count);
  [NewObject] IDBRequest getAllKeys(optional any query,
                                    optional [EnforceRange] unsigned long count);
  [NewObject] IDBRequest count(optional any query);

  [NewObject] IDBRequest openCursor(optional any query,
                                    optional IDBCursorDirection direction = "next");
  [NewObject] IDBRequest openKeyCursor(optional any query,
                                       optional IDBCursorDirection direction = "next");
};

參考:

idb 開(kāi)源庫(kù),微型代碼庫(kù)
treo 開(kāi)源庫(kù)
dexie.js 開(kāi)源庫(kù)
indexeddb
原生概念 IndexedDB

也歡迎大家關(guān)注我的公眾號(hào):前端小吉米 獲得一手的技術(shù)文章以及未來(lái)技術(shù)的發(fā)展內(nèi)容。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/95152.html

相關(guān)文章

  • 【譯】漸進(jìn)式 Web App 的離線存儲(chǔ)

    摘要:離線存儲(chǔ)數(shù)據(jù)的建議對(duì)尋址資源,使用這是的一部分。在到達(dá)儲(chǔ)量限制之前,兩種存儲(chǔ)機(jī)制都會(huì)一直進(jìn)行存儲(chǔ)。則沒(méi)有對(duì)存儲(chǔ)量做出限制,只是在之后會(huì)彈出提醒。是異步的基于回調(diào)函數(shù),但它同樣不支持。也是異步的基于回調(diào)函數(shù),在和中可以工作雖然使用的是同步。 拖拖拉拉好久,終于把個(gè)人博客整出來(lái)了。鳴謝 @pinggod。 厚著臉安利一下,地址是 http://www.wemlion.com/。歡迎訪問(wèn),歡...

    Joyven 評(píng)論0 收藏0
  • 【譯】漸進(jìn)式 Web App 的離線存儲(chǔ)

    摘要:離線存儲(chǔ)數(shù)據(jù)的建議對(duì)尋址資源,使用這是的一部分。在到達(dá)儲(chǔ)量限制之前,兩種存儲(chǔ)機(jī)制都會(huì)一直進(jìn)行存儲(chǔ)。則沒(méi)有對(duì)存儲(chǔ)量做出限制,只是在之后會(huì)彈出提醒。是異步的基于回調(diào)函數(shù),但它同樣不支持。也是異步的基于回調(diào)函數(shù),在和中可以工作雖然使用的是同步。 拖拖拉拉好久,終于把個(gè)人博客整出來(lái)了。鳴謝 @pinggod。 厚著臉安利一下,地址是 http://www.wemlion.com/。歡迎訪問(wèn),歡...

    Charlie_Jade 評(píng)論0 收藏0
  • 創(chuàng)建一個(gè)離線優(yōu)先,數(shù)據(jù)驅(qū)動(dòng)的漸進(jìn)式 Web 應(yīng)用程序

    摘要:原文地址譯文出自我的個(gè)人博客概述在本文中,您將學(xué)習(xí)如何使用和創(chuàng)建離線優(yōu)先數(shù)據(jù)驅(qū)動(dòng)的漸進(jìn)式應(yīng)用程序。在離線的情況下也可以使用后臺(tái)同步功能將應(yīng)用程序與服務(wù)器同步。保存數(shù)據(jù)到中現(xiàn)在我們保存數(shù)據(jù)到剛創(chuàng)建的數(shù)據(jù)庫(kù)中的對(duì)象中。 原文地址:Build an offline-first, data-driven PWA譯文出自:我的個(gè)人博客 概述 在本文中,您將學(xué)習(xí)如何使用 Workbox 和 In...

    csRyan 評(píng)論0 收藏0
  • 前端進(jìn)階系列(三):HTML5新特性

    摘要:是對(duì)標(biāo)準(zhǔn)的第五次修訂。新特性語(yǔ)義特性賦予網(wǎng)頁(yè)更好的意義和結(jié)構(gòu)文件類(lèi)型聲明僅有一型。新的屬性用于與用于用于。索引數(shù)據(jù)庫(kù)從本質(zhì)上說(shuō),允許用戶在瀏覽器中保存大量的數(shù)據(jù)。 HTML5 是對(duì) HTML 標(biāo)準(zhǔn)的第五次修訂。其主要的目標(biāo)是將互聯(lián)網(wǎng)語(yǔ)義化,以便更好地被人類(lèi)和機(jī)器閱讀,并同時(shí)提供更好地支持各種媒體的嵌入。HTML5 的語(yǔ)法是向后兼容的?,F(xiàn)在國(guó)內(nèi)普遍說(shuō)的 H5 是包括了 CSS3,Java...

    spademan 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<