摘要:本文轉(zhuǎn)載自眾成翻譯譯者文藺鏈接原文介紹本文是關(guān)于客戶端存儲(chǔ)的。因此,瀏覽器對(duì)存儲(chǔ)容量施加了限制。例如,可能會(huì)允許最多存的,的數(shù)據(jù)庫,但因用戶拒絕訪問被禁止使用。語義事件可保持其他標(biāo)簽和窗口同步。
介紹本文轉(zhuǎn)載自:眾成翻譯
譯者:文藺
鏈接:http://www.zcfy.cc/article/660
原文:http://www.html5rocks.com/en/tutorials/offline/storage/
本文是關(guān)于客戶端存儲(chǔ)(client-side storage)的。這是一個(gè)通用術(shù)語,包含幾個(gè)獨(dú)立但相關(guān)的 API: Web Storage、Web SQL Database、Indexed Database 和 File Access。每種技術(shù)都提供了在用戶硬盤上 —— 而非通常存儲(chǔ)數(shù)據(jù)的服務(wù)器 —— 存儲(chǔ)數(shù)據(jù)的獨(dú)特方式。這么做主要基于以下兩點(diǎn)理由:(a)使 web app 離線可用; (b)改善性能。對(duì)于客戶端存儲(chǔ)使用情況的詳細(xì)闡述,請(qǐng)看 HTML5Rocks 上的文章 《"離線": 這是什么意思?我為何要關(guān)心?》。
這些 API 有著類似的作用范圍和規(guī)則。因此,在去看細(xì)節(jié)之前,我們先了解他們的共同之處吧。
共同特點(diǎn) 基于客戶端的存儲(chǔ)實(shí)際上,“客戶端時(shí)間存儲(chǔ)”的意思是,數(shù)據(jù)傳給了瀏覽器的存儲(chǔ) API,它將數(shù)據(jù)存在本地設(shè)備中的一塊區(qū)域,該區(qū)域同樣也是它存儲(chǔ)其他用戶特定信息如個(gè)人偏好、緩存的地方。除了存儲(chǔ)數(shù)據(jù),這些 API 可以用來檢索數(shù)據(jù),且在某些情況下還能執(zhí)行搜索和批處理操作。
置于沙盒中的所有這四個(gè)存儲(chǔ) API 都將數(shù)據(jù)綁到一個(gè)多帶帶的“源”(origin)上。例如,若 http://abc.example.com 保存了一些數(shù)據(jù),那以后瀏覽器就只會(huì)允許 http://abc.example.com 獲取這些數(shù)據(jù)。當(dāng)我們談?wù)摗霸础保╫rigin)的時(shí)候,這意味著域(domain)必須完全相同,所以 http://example.com 和 http://def.example.com 都不行。端口(port)也必須匹配,因此 http://abc.example.com:123 也是不能訪問到 http://abc.example.com (端口默認(rèn)為80)存儲(chǔ)的數(shù)據(jù)。同樣,協(xié)議也必須一樣(像http vs https 等等)。
空間限制(Quotas)你能想象,如果任何網(wǎng)站都被允許往毫不知情的硬盤里填充以千兆字節(jié)計(jì)的數(shù)據(jù),該有多混亂。因此,瀏覽器對(duì)存儲(chǔ)容量施加了限制。若你的應(yīng)用試圖超出限制,瀏覽器通常會(huì)顯示一個(gè)對(duì)話框,讓用戶確認(rèn)增加。您可能以為瀏覽器對(duì)單個(gè)源(origin)可使用的所有存儲(chǔ)都加以同一多帶帶的限制,但多數(shù)存儲(chǔ)機(jī)制都是多帶帶加以限制的。若 Quota API 被采納,這種情況可能會(huì)改變。但就現(xiàn)在來說,把瀏覽器當(dāng)作一個(gè)二維矩陣,其維度分別是“源”(origin)和“存儲(chǔ)”(storage)。例如, "http://abc.example.com" 可能會(huì)允許最多存 5MB 的 Web Storage, 25MB 的 Web SQL 數(shù)據(jù)庫,但因用戶拒絕訪問被禁止使用 Indexed DataBase。 Quota API 將問題放到一起來看,讓您查詢還有多少可用空間,有多少空間正在使用。
有些情況下,用戶也能先看到有多少存儲(chǔ)將被使用,例如,當(dāng)用戶在 Chrome 應(yīng)用商店中安裝一個(gè)應(yīng)用時(shí),他們將被提示預(yù)先接受其權(quán)限,其中包括存儲(chǔ)限制。(而該應(yīng)用的)manifest 中的可能有個(gè)值是 “unlimited_storage” (無限制存儲(chǔ))。
數(shù)據(jù)庫處理(Transactions)兩個(gè) “數(shù)據(jù)庫” 的存儲(chǔ)格式支持?jǐn)?shù)據(jù)處理。目的和通常的關(guān)系型數(shù)據(jù)庫使用數(shù)據(jù)處理是一樣的:保證數(shù)據(jù)庫完整。數(shù)據(jù)庫處理(Transactions)防止 “競(jìng)爭(zhēng)條件”(race conditions) —— 這種情況是:當(dāng)兩個(gè)操作序列在同一時(shí)間被應(yīng)用到數(shù)據(jù)庫中, 導(dǎo)致操作結(jié)果都無法被預(yù)測(cè),而數(shù)據(jù)庫也處于可疑的準(zhǔn)確性(dubious accuracy)狀態(tài)。
同步和異步模式(Synchronous and Asynchronous Modes)多數(shù)存儲(chǔ)格式都支持同步和異步模式。同步模式是阻塞的,意味著下一行 js 代碼執(zhí)行之前,存儲(chǔ)操作會(huì)被完整執(zhí)行。異步模式會(huì)使得后面的 js 代碼在數(shù)據(jù)庫操作完成之前執(zhí)行。存儲(chǔ)操作會(huì)背景環(huán)境中執(zhí)行,當(dāng)操作完成的時(shí)候,應(yīng)用會(huì)以回調(diào)函數(shù)被調(diào)用這種形式接收通知,這個(gè)函數(shù)須在調(diào)用的時(shí)候被指定。
應(yīng)當(dāng)盡量避免使用同步模式,它雖然看起來比較簡(jiǎn)單,但操作完成時(shí)它會(huì)阻塞頁面渲染,在某些情況下甚至?xí)鼋Y(jié)整個(gè)瀏覽器。你可能注意到網(wǎng)站乃至是應(yīng)用出現(xiàn)這種情況,點(diǎn)擊一個(gè)按鈕,結(jié)果所有東西都用不了,當(dāng)你還在想是不是崩潰了?結(jié)果一切又突然恢復(fù)正常了。
某些 API 沒有異步模式,如 “l(fā)ocalStorage”, 使用這些API時(shí),應(yīng)當(dāng)仔細(xì)做好性能監(jiān)測(cè),并隨時(shí)準(zhǔn)備切換到一個(gè)異步API,如果它造成了問題。
API 概述及比較 Web StorageWeb Storage 是一個(gè)叫做 localStorage 的持久對(duì)象。可以使用 localStorage.foo = "bar" 保存值,之后可以使用 localStorage.foo 獲取到 —— 甚至是瀏覽器關(guān)閉之后重新打開。還可以使用一個(gè)叫做 sessionStorage 的對(duì)象,工作方式一樣,只是當(dāng)窗口關(guān)閉之后會(huì)被清除掉。
Web Storage 是 NoSQL 鍵值對(duì)儲(chǔ)存(NoSQL key-value store)的一種.
數(shù)年以來,被所有現(xiàn)代瀏覽器支持, iOS 和 Android 系統(tǒng)下也支持(IE 從 IE8 開始支持 )。
簡(jiǎn)單的API簽名。
同步 API,調(diào)用簡(jiǎn)單。
語義事件可保持其他標(biāo)簽和窗口同步。
使用同步 API(這是得到最廣泛支持的模式)存儲(chǔ)大量的或者復(fù)雜的數(shù)據(jù)時(shí)性能差。
缺少索引導(dǎo)致檢索大量的或復(fù)雜的數(shù)據(jù)時(shí)性能差。(搜索操作需要手動(dòng)遍歷所有項(xiàng)。)
存儲(chǔ)或讀取大量的或復(fù)雜的數(shù)據(jù)結(jié)構(gòu)時(shí)性能差,因?yàn)樾枰謩?dòng)序序列化成字符串或?qū)⒆址葱蛄谢V饕臑g覽器實(shí)現(xiàn)只支持字符串(盡管規(guī)范沒這么說的)。
需要保證數(shù)據(jù)的持續(xù)性和完整性,因?yàn)閿?shù)據(jù)是有效非結(jié)構(gòu)化(effectively unstructured)的。
Web SQL DatabaseWeb SQL Database 是一個(gè)結(jié)構(gòu)化的數(shù)據(jù)庫,具備典型 SQL驅(qū)動(dòng)的關(guān)系數(shù)據(jù)庫(SQL-powered relational database)的所有功能和復(fù)雜度。Indexed Database 在兩者之間。Web SQL Database 有自由形式的密鑰值對(duì),有點(diǎn)像 Web Storage,但也有能力從這些值來索引字段,所以搜索速度要快得多。
被主要的移動(dòng)瀏覽器(Android Browser, Mobile Safari, Opera Mobile)以及一些 PC 瀏覽器(Chrome, Safari, Opera) 支持。
作為異步 API, 總體而言性能很好。數(shù)據(jù)庫交互不會(huì)鎖定用戶界面。(同步API也可用于 WebWorkers。)
良好的搜索性能,因?yàn)閿?shù)據(jù)可以根據(jù)搜索鍵進(jìn)行索引。
強(qiáng)大,因?yàn)樗С质聞?wù)性數(shù)據(jù)庫模型(transactional database model)。
剛性的數(shù)據(jù)結(jié)構(gòu)更容易保持?jǐn)?shù)據(jù)的完整性。
過時(shí),不會(huì)被 IE 或 Firefox 支持,在某些階段可能會(huì)被從其他瀏覽器淘汰。
學(xué)習(xí)曲線陡峭,要求掌握關(guān)系數(shù)據(jù)庫和SQL的知識(shí)。
對(duì)象-關(guān)系阻抗失配(object-relational impedance mismatch).
降低敏捷性,因?yàn)閿?shù)據(jù)庫模式必須預(yù)先定義,與表中的所有記錄必須匹配相同的結(jié)構(gòu)。
Indexed Database (IndexedDB)到目前為止,我們已經(jīng)看到,Web Storage 和 Web SQL Database 都有各種的優(yōu)勢(shì)和弱點(diǎn)。 Indexed Database 產(chǎn)生于這兩個(gè)早期 API 的經(jīng)驗(yàn),可以看作是一種結(jié)合兩者優(yōu)點(diǎn)而不招致其劣勢(shì)得到嘗試。
Indexed Database 是一個(gè) “對(duì)象存儲(chǔ)” (object stores) 的集合,可以直接把對(duì)象放進(jìn)去。這個(gè)存儲(chǔ)有點(diǎn)像 SQL 表,但在這種情況下,對(duì)象的結(jié)構(gòu)沒有約束,所以不需要預(yù)先定義什么。所以這和 Web Storage 有點(diǎn)像,擁有多個(gè)數(shù)據(jù)庫、每個(gè)數(shù)據(jù)庫又有多個(gè)存儲(chǔ)(store)的特點(diǎn)。但不像 Web Storage那樣, 還擁有重要的性能優(yōu)勢(shì): 異步接口,可以在存儲(chǔ)上創(chuàng)建索引,以提高搜索速度。
作為異步API總體表現(xiàn)良好。數(shù)據(jù)庫交互不會(huì)鎖定用戶界面。(同步 API 也可用于 WebWorkers。)
良好的搜索性能,因?yàn)閿?shù)據(jù)可以根據(jù)搜索鍵進(jìn)行索引。
支持版本控制。
強(qiáng)大,因?yàn)樗С质聞?wù)性數(shù)據(jù)庫模型(transactional database model)。
因?yàn)閿?shù)據(jù)模型簡(jiǎn)單,學(xué)習(xí)曲線也相當(dāng)簡(jiǎn)單。
良好的瀏覽器支持: Chrome, Firefox, mobile FF, IE10.
非常復(fù)雜的API,導(dǎo)致大量的嵌套回調(diào)。
FileSystem上面的 API 都是適用于文本和結(jié)構(gòu)化數(shù)據(jù),但涉及到大文件和二進(jìn)制內(nèi)容時(shí),我們需要一些其他的東西。幸運(yùn)的是,我們現(xiàn)在有了文件系統(tǒng) API 標(biāo)準(zhǔn)(FileSystem API standard)。它給每個(gè)域一個(gè)完整的層次化的文件系統(tǒng),至少在 Chrome 下面,這些都是用戶的硬盤上的真正的文件。就單個(gè)文件的讀寫而言, API 建立在現(xiàn)有的 File API之上。
可以存儲(chǔ)大量的內(nèi)容和二進(jìn)制文件,很適合圖像,音頻,視頻,PDF,等。
作為異步 API, 性能良好。
很早的標(biāo)準(zhǔn),只有 Chrome 和 Opera 支持。
沒有事務(wù)(transaction)支持。
沒有內(nèi)建的搜索/索引支持。
來看代碼本部分比較不同的 API 如何解決同一個(gè)問題。這個(gè)例子是一個(gè) “地理情緒”(geo-mood) 簽到系統(tǒng),在那里你可以記錄你在時(shí)間和地點(diǎn)的情緒。接口可讓你在數(shù)據(jù)庫類型之間切換。當(dāng)然,在現(xiàn)實(shí)情況中,這可能顯得有點(diǎn)作(contrived),數(shù)據(jù)庫類型肯定比其他的更有意義,文件系統(tǒng) API 根本不適用于這種應(yīng)用!但為了演示的目的,如果我們能看到使用不同方式達(dá)到同樣的結(jié)果,這還是有幫助的。還得注意,為了保值可讀性,一些代碼片段是經(jīng)過重構(gòu)的。
現(xiàn)在可以來試試我們的“地理情緒”(geo-mood)應(yīng)用。
為了讓 Demo 更有意思,我們將數(shù)據(jù)存儲(chǔ)多帶帶拿出來,使用標(biāo)準(zhǔn)的面向?qū)ο蟮脑O(shè)計(jì)技術(shù)(standard object-oriented design techniques)。 UI 邏輯只知道有一個(gè) store;它無需知道 store 是如何實(shí)現(xiàn)的,因?yàn)槊總€(gè) store 的方法是一樣的。因此 UI 層代碼可以稱為 store.setup(),store.count() 等等。實(shí)際上,我們的 store 有四種實(shí)現(xiàn),每種對(duì)應(yīng)一種存儲(chǔ)類型。應(yīng)用啟動(dòng)的時(shí)候,檢查 URL 并實(shí)例化對(duì)應(yīng)的 store。
為了保持 API 的一致性,所有的方法都是異步的,即它們將結(jié)果返回給調(diào)用方。Web Storage 的實(shí)現(xiàn)甚至也是這樣的,其底層實(shí)現(xiàn)是本地的。
在下面的演示中,我們將跳過 UI 和定位邏輯,聚焦于存儲(chǔ)技術(shù)。
建立 Store對(duì) localStorage,我們做個(gè)簡(jiǎn)單的檢驗(yàn)看存儲(chǔ)是否存在。如果不存在,則新建一個(gè)數(shù)組,并將其存儲(chǔ)在 localStorage 的 checkins(簽到) 鍵下面。首先,我們使用 JSON 對(duì)象將結(jié)構(gòu)序列化為字符串,因?yàn)榇蠖鄶?shù)瀏覽器只支持字符串存儲(chǔ)。
if (!localStorage.checkins) localStorage.checkins = JSON.stringify([]);
對(duì) Web SQL Database,數(shù)據(jù)庫結(jié)構(gòu)如果不存在的話,我們需要先創(chuàng)建。幸運(yùn)的是,如果數(shù)據(jù)庫不存在,openDatabase 方法會(huì)自動(dòng)創(chuàng)建數(shù)據(jù)庫;同樣,使用 SQL 句 “if not exists” 可以確保新的 checkins 表 如果已經(jīng)存在的話不會(huì)被重寫。我們需要預(yù)先定義好數(shù)據(jù)結(jié)構(gòu),也就是, checkins 表每列的名稱和類型。每一行數(shù)據(jù)代表一次簽到。
this.db = openDatabase("geomood", "1.0", "Geo-Mood Checkins", 8192); this.db.transaction(function(tx) { tx.executeSql( "create table if not exists " + "checkins(id integer primary key asc, time integer, latitude float," + "longitude float, mood string)", [], function() { console.log("siucc"); } ); });
Indexed Database 啟動(dòng)需要一些工作,因?yàn)樗枰獑⒂靡粋€(gè)數(shù)據(jù)庫版本系統(tǒng)。當(dāng)我們連接數(shù)據(jù)庫的時(shí)候要明確我們需要那個(gè)版本,如果當(dāng)前數(shù)據(jù)庫使用的是之前的版本或者還尚未被創(chuàng)建,會(huì)觸發(fā) onupgradeneeded 事件,當(dāng)升級(jí)完成后 onsuccess 事件會(huì)被觸發(fā)。如果無需升級(jí),onsuccess 事件馬上就會(huì)觸發(fā)。
另外一件事就是創(chuàng)建 “mood” 索引,以便之后能很快地查詢到匹配的情緒。
var db; var version = 1; window.indexedStore = {}; window.indexedStore.setup = function(handler) { // attempt to open the database var request = indexedDB.open("geomood", version); // upgrade/create the database if needed request.onupgradeneeded = function(event) { var db = request.result; if (event.oldVersion < 1) { // Version 1 is the first version of the database. var checkinsStore = db.createObjectStore("checkins", { keyPath: "time" }); checkinsStore.createIndex("moodIndex", "mood", { unique: false }); } if (event.oldVersion < 2) { // In future versions we"d upgrade our database here. // This will never run here, because we"re version 1. } db = request.result; }; request.onsuccess = function(ev) { // assign the database for access outside db = request.result; handler(); db.onerror = function(ev) { console.log("db error", arguments); }; }; };
最后,啟動(dòng) FileSystem。我們會(huì)把每種簽到 JSON 編碼后放在多帶帶的文件中,它們都在 “checkins/” 目錄下面。同樣這并非 FileSystem API 最合適的用途,但對(duì)演示來說還挺好。
啟動(dòng)在整個(gè)文件系統(tǒng)中拿到一個(gè)控制手柄(handle),用來檢查 “checkins/” 目錄。如果目錄不存在,使用 getDirectory 創(chuàng)建。
setup: function(handler) { requestFileSystem( window.PERSISTENT, 1024*1024, function(fs) { fs.root.getDirectory( "checkins", {}, // no "create" option, so this is a read op function(dir) { checkinsDir = dir; handler(); }, function() { fs.root.getDirectory( "checkins", {create: true}, function(dir) { checkinsDir = dir; handler(); }, onError ); } ); }, function(e) { console.log("error "+e.code+"initialising - see http://goo.gl/YW0TI"); } ); }保存一次簽到 (Check-in)
使用 localStorage,我們只需要拿出 check-in 數(shù)組,在尾部添加一個(gè),然后重新保存就行。我們還需要使用 JSON 對(duì)象的方法將其以字符串的方式存起來。
var checkins = JSON.parse(localStorage["checkins"]); checkins.push(checkin); localStorage["checkins"] = JSON.stringify(checkins);
使用 Web SQL Database,所有的事情都在 transaction 中進(jìn)行。我們要在 checkins 表 創(chuàng)建新的一行,這是一個(gè)簡(jiǎn)單的 SQL 調(diào)用,我們使用 “?” 語法,而不是把所有的簽到數(shù)據(jù)都放到 “insert” 命令中,這樣更整潔,也更安全。真正的數(shù)據(jù)——我們要保存的四個(gè)值——被放到第二行。“?” 元素會(huì)被這些值(checkin.time,checkin.latitude等等)替換掉。接下來的兩個(gè)參數(shù)是操作完成之后被調(diào)用的函數(shù),分別在成功和失敗后調(diào)用。在這個(gè)應(yīng)用中,我們對(duì)所有操作使用相同的通用錯(cuò)誤處理程序。這樣,成功回調(diào)函數(shù)就是我們傳給搜索函數(shù)的句柄——確保句柄在成功的時(shí)候被調(diào)用,以便操作完成之后 UI 能接到通知(比如,更新目前為止的簽到數(shù)量)。
store.db.transaction(function(tx) { tx.executeSql( "insert into checkins " + "(time, latitude, longitude, mood) values (?,?,?,?);", [checkin.time, checkin.latitude, checkin.longitude, checkin.mood], handler, store.onError ); });
一旦存儲(chǔ)建立起來,將其存儲(chǔ)到 IndexedDB 中就像 Web Storage 差不多簡(jiǎn)單,還有異步工作的優(yōu)點(diǎn)。
var transaction = db.transaction("checkins", "readwrite"); transaction.objectStore("checkins").put(checkin); transaction.oncomplete = handler;
使用 FileSystem API,新建文件并拿到相應(yīng)的句柄,可以用 FileWriter API 進(jìn)行填充。
fs.root.getFile( "checkins/" + checkin.time, { create: true, exclusive: true }, function(file) { file.createWriter(function(writer) { writer.onerror = fileStore.onError; var bb = new WebKitBlobBuilder; bb.append(JSON.stringify(checkin)); writer.write(bb.getBlob("text/plain")); handler(); }, fileStore.onError); }, fileStore.onError );搜索匹配項(xiàng)
接下來的函數(shù)找到所有匹配特定情緒的簽到,例如,用戶能看到他們?cè)谧罱螘r(shí)何地過得很開心。使用 localStorage, 我們必須手動(dòng)遍歷每次簽到并將其與搜索的情緒對(duì)比,建立一個(gè)匹配列表。比較好的實(shí)踐是返回存儲(chǔ)數(shù)據(jù)的克隆,而不是實(shí)際的對(duì)象,因?yàn)樗阉鲬?yīng)該是一個(gè)只讀的操作;所以我們將每個(gè)匹配的簽到對(duì)象傳遞給通用的 clone() 方法進(jìn)行操作。
var allCheckins = JSON.parse(localStorage["checkins"]); var matchingCheckins = []; allCheckins.forEach(function(checkin) { if (checkin.mood == moodQuery) { matchingCheckins.push(clone(checkin)); } }); handler(matchingCheckins);
使用 Web SQL Database,我們執(zhí)行一次查詢,只返回我們需要的行。但我們?nèi)孕枰謩?dòng)遍歷來累計(jì)簽到數(shù)據(jù),因?yàn)閿?shù)據(jù)庫 API 返回的是數(shù)據(jù)庫行,而不是一個(gè)數(shù)組。(對(duì)大的結(jié)果集來說這是好事,但就現(xiàn)在而言這增加了我們需要的工作!)
var matchingCheckins = []; store.db.transaction(function(tx) { tx.executeSql( "select * from checkins where mood=?", [moodQuery], function(tx, results) { for (var i = 0; i < results.rows.length; i++) { matchingCheckins.push(clone(results.rows.item(i))); } handler(matchingCheckins); }, store.onError ); });
當(dāng)然,在 IndexedDB 解決方案使用索引,我們先前在 “mood” 表中創(chuàng)建的索引,稱為“moodindex”。我們用一個(gè)指針遍歷每次簽到以匹配查詢。注意這個(gè)指針模式也可以用于整個(gè)存儲(chǔ);因此,使用索引就像我們?cè)谏痰昀锏囊粋€(gè)窗口前,只能看到匹配的對(duì)象(類似于在傳統(tǒng)數(shù)據(jù)庫中的“視圖”)。
var store = db.transaction("checkins", "readonly").objectStore("checkins"); var request = moodQuery ? store.index("moodIndex").openCursor(new IDBKeyRange.only(moodQuery)) : store.openCursor(); request.onsuccess = function(ev) { var cursor = request.result; if (cursor) { handler(cursor.value); cursor["continue"](); } };
與許多傳統(tǒng)的文件系統(tǒng)一樣,F(xiàn)ileSystem API 沒有索引,所以搜索算法(如 Unix中的 “grep” 命令)必須遍歷每個(gè)文件。我們從 “checkins/” 目錄中拿到 Reader API ,通過 readentries() 。對(duì)于每個(gè)文件,再使用一個(gè) reader,使用 readastext() 方法檢查其內(nèi)容。這些操作都是異步的,我們需要使用 readnext() 將調(diào)用連在一起。
checkinsDir.createReader().readEntries(function(files) { var reader, fileCount = 0, checkins = []; var readNextFile = function() { reader = new FileReader(); if (fileCount == files.length) return; reader.onload = function(e) { var checkin = JSON.parse(this.result); if (moodQuery == checkin.mood || !moodQuery) handler(checkin); readNextFile(); }; files[fileCount++].file(function(file) { reader.readAsText(file); }); }; readNextFile(); });匹配計(jì)數(shù)
最后,我們需要給所有簽到計(jì)數(shù)。
對(duì)localStorage,我們簡(jiǎn)單的反序列化簽到數(shù)組,讀取其長度。
handler(JSON.parse(localStorage["checkins"]).length);
對(duì) Web SQL Database,可以檢索數(shù)據(jù)庫中的每一行(select * from checkins),看結(jié)果集的長度。但如果我們知道我們?cè)?SQL 中,有更容易和更快的方式 —— 我們可以執(zhí)行一個(gè)特殊的 select 語句來檢索計(jì)數(shù)。它將返回一行,其中一列包含計(jì)數(shù)。
store.db.transaction(function(tx) { tx.executeSql("select count(*) from checkins;", [], function(tx, results) { handler(results.rows.item(0)["count(*)"]); }, store.onError); });
不幸的是, IndexedDB 不提供任何計(jì)算方法,所以我們只能自己遍歷。
var count = 0; var request = db.transaction(["checkins"], "readonly").objectStore("checkins").openCursor(); request.onsuccess = function(ev) { var cursor = request.result; cursor ? ++count && cursor["continue"]() : handler(count); };
對(duì)于文件系統(tǒng), directory reader 的 readentries() 方法提供一個(gè)文件列表,所以我們返回該列表的長度就好。
checkinsDir.createReader().readEntries(function(files) { handler(files.length); });總結(jié)
本文從較高層次的角度,講述了現(xiàn)代客戶端存儲(chǔ)技術(shù)。你也可以看看 《離線應(yīng)用概述》(overview on offline apps)這篇文章。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/79781.html
摘要:在上一篇,介紹一下漸進(jìn)式離線的文章中,我們討論了典型的應(yīng)該是什么樣子的并且同時(shí)也介紹了。暴露了一個(gè)異步,以避免阻塞的加載。但一些研究表明,在某些情況下,它是阻塞的。打開并且添加如下代碼清除緩存并重新加載。 在上一篇,介紹一下漸進(jìn)式 Web App(離線) - Part 1的文章中,我們討論了典型的pwa應(yīng)該是什么樣子的并且同時(shí)也介紹了 server worker。到目前為止,我們已經(jīng)緩...
摘要:在上一篇,介紹一下漸進(jìn)式離線的文章中,我們討論了典型的應(yīng)該是什么樣子的并且同時(shí)也介紹了。暴露了一個(gè)異步,以避免阻塞的加載。但一些研究表明,在某些情況下,它是阻塞的。打開并且添加如下代碼清除緩存并重新加載。 在上一篇,介紹一下漸進(jìn)式 Web App(離線) - Part 1的文章中,我們討論了典型的pwa應(yīng)該是什么樣子的并且同時(shí)也介紹了 server worker。到目前為止,我們已經(jīng)緩...
摘要:在上一篇,介紹一下漸進(jìn)式離線的文章中,我們討論了典型的應(yīng)該是什么樣子的并且同時(shí)也介紹了。暴露了一個(gè)異步,以避免阻塞的加載。但一些研究表明,在某些情況下,它是阻塞的。打開并且添加如下代碼清除緩存并重新加載。 在上一篇,介紹一下漸進(jìn)式 Web App(離線) - Part 1的文章中,我們討論了典型的pwa應(yīng)該是什么樣子的并且同時(shí)也介紹了 server worker。到目前為止,我們已經(jīng)緩...
閱讀 1842·2021-11-11 16:55
閱讀 764·2019-08-30 15:53
閱讀 3604·2019-08-30 15:45
閱讀 753·2019-08-30 14:10
閱讀 3279·2019-08-30 12:46
閱讀 2135·2019-08-29 13:15
閱讀 2037·2019-08-26 13:48
閱讀 947·2019-08-26 12:23