Web應用中的離線資料儲存

njuyz發表於2014-02-15

為了提升Web應用的使用者體驗,想必很多開發者都會專案中引入離線資料儲存機制。可是面對各種各樣的離線資料技術,哪一種才是最能滿足專案需求的呢?本文將幫助各位找到最合適的那一個。

引言

隨著HTML5的到來,各種Web離線資料技術進入了開發人員的視野。諸如AppCache、localStorage、sessionStorage和IndexedDB等等,每一種技術都有它們各自適用的範疇。比如AppCache就比較適合用於離線起動應用,或者在離線狀態下使應用的一部分功能照常執行。接下來我將會為大家作詳細介紹,並且用一些程式碼片段來展示如何使用這些技術。

AppCache

如果你的Web應用中有一部分功能(或者整個應用)需要在脫離伺服器的情況下使用,那麼就可以通過AppCache來讓你的使用者在離線狀態下也能使用。你所需要做的就是建立一個配置檔案,在其中指定哪些資源需要被快取,哪些不需要。此外,還能在其中指定某些聯機資源在離線條件下的替代資源。

AppCache的配置檔案通常是一個以.appcache結尾的文字檔案(推薦寫法)。檔案以CACHE MANIFEST開頭,包含下列三部分內容:

  • CACHE – 指定了哪些資源在使用者第一次訪問站點的時候需要被下載並快取
  • NETWORK – 指定了哪些資源需要在聯機條件下才能訪問,這些資源從不被快取
  • FALLBACK – 指定了上述資源在離線條件下的替代資源

示例

首先,你需要在頁面上指定AppCache的配置檔案:

在這裡千萬記得在伺服器端釋出上述配置檔案的時候,需要將MIME型別設定為text/cache-manifest,否則瀏覽器無法正常解析。

接下來是建立之前定義好的各種資源。我們假定在這個示例中,你開發的是一個互動類站點,使用者可以在上面聯絡別人並且發表評論。使用者在離線的狀態下依然可以訪問網站的靜態部分,而聯絡以及發表評論的頁面則會被其它頁面替代,無法訪問。

好的,我們這就著手定義那些靜態資源:

旁註:配置檔案寫起來有一點很不方便。舉例來說,如果你想快取整個目錄,你不能直接在CACHE部分使用萬用字元(*),而是隻能在NETWORK部分使用萬用字元把所有不應該被快取的資源寫出來。

你不需要顯式地快取包含配置檔案的頁面,因為這個頁面會自動被快取。接下來我們為聯絡和評論的頁面定義FALLBACK部分:

最後我們用一個萬用字元來阻止其餘的資源被快取:

最後的結果就是下面這樣:

還有一件很重要的事情要記得:你的資源只會被快取一次!也就是說,如果資源更新了,它們不會自動更新,除非你修改了配置檔案。所以有一個最佳實踐是,在配置檔案中增加一項版本號,每次更新資源的時候順帶更新版本號:

LocalStorage和SessionStorage

如果你想在Javascript程式碼裡面儲存些資料,那麼這兩個東西就派上用場了。前一個可以儲存資料,永遠不會過期(expire)。只要是相同的域和埠,所有的頁面中都能訪問到通過LocalStorage儲存的資料。舉個簡單的例子,你可以用它來儲存使用者設定,使用者可以把他的個人喜好儲存在當前使用的電腦上,以後開啟應用的時候能夠直接載入。後者也能儲存資料,但是一旦關閉瀏覽器視窗(譯者注:瀏覽器視窗,window,如果是多tab瀏覽器,則此處指代tab)就失效了。而且這些資料不能在不同的瀏覽器視窗之間共享,即使是在不同的視窗中訪問同一個Web應用的其它頁面。

旁註:有一點需要提醒的是,LocalStorage和SessionStorage裡面只能儲存基本型別的資料,也就是字串和數字型別。其它所有的資料可以通過各自的toString()方法轉化後儲存。如果你想儲存一個物件,則需要使用JSON.stringfy方法。(如果這個物件是一個類,你可以複寫它預設的toString()方法,這個方法會自動被呼叫)。

示例

我們不妨來看看之前的例子。在聯絡人和評論的部分,我們可以隨時儲存使用者輸入的東西。這樣一來,即使使用者不小心關閉了瀏覽器,之前輸入的東西也不會丟失。對於jQuery來說,這個功能是小菜一碟。(注意:表單中每個輸入欄位都有id,在這裡我們就用id來指代具體的欄位)

每次提交聯絡人和評論的表單,我們需要清空快取的值,我們可以這樣處理提交(submit)事件:

最後,每次載入頁面的時候,把快取的值填充到表單上即可:

IndexedDB

在我個人看來,這是最有意思的一種技術。它可以儲存大量經過索引(indexed)的資料在瀏覽器端。這樣一來,就能在客戶端儲存複雜物件,大文件等等資料。而且使用者可以在離線情況下訪問它們。這一特性幾乎適用於所有型別的Web應用:如果你寫的是郵件客戶端,你可以快取使用者的郵件,以供稍後再看;如果你寫的是相簿類應用,你可以離線儲存使用者的照片;如果你寫的是GPS導航,你可以快取使用者的路線……不勝列舉。

IndexedDB是一個物件導向的資料庫。這就意味著在IndexedDB中既不存在表的概念,也沒有SQL,資料是以鍵值對的形式儲存的。其中的鍵既可以是字串和數字等基礎型別,也可以是日期和陣列等複雜型別。這個資料庫本身構建於儲存(store,一個store類似於關係型資料中表的概念)的基礎上。資料庫中每個值都必須要有對應的鍵。每個鍵既可以自動生成,也可以在插入值的時候指定,也可以取自於值中的某個欄位。如果你決定使用值中的欄位,那麼只能向其中新增Javascript物件,因為基礎資料型別不像Javascript物件那樣有自定義屬性。

示例

在這個例子中,我們用一個音樂專輯應用作為示範。不過我並不打算在這裡從頭到尾展示整個應用,而是把涉及IndexedDB的部分挑出來解釋。如果大家對這個Web應用感興趣的話,文章的後面也提供了原始碼的下載。首先,讓我們來開啟資料庫並建立store:

相信上面的程式碼還是相當通俗易懂的。估計你也注意到上述程式碼中開啟資料庫時會傳入一個版本號,還用到了onupgradeneeded事件。當你以較新的版本開啟資料庫時就會觸發這個事件。如果相應版本的資料庫尚不存在,則會觸發事件,隨後我們就會建立所需的store。接下來我們還建立了兩個索引,一個用於標題搜尋,一個用於樂隊搜尋。現在讓我們再來看看如何增加和刪除專輯:

是不是看起來直接明瞭?這裡對資料庫所有的操作都基於事務的,只有這樣才能保證資料的一致性。現在最後要做的就是展示音樂專輯:

‘); // move to the next item in the cursor cursor.continue(); } }; }

這也不是十分複雜。可以看見,通過使用IndexedDB,可以很輕鬆的儲存複雜物件,也可以通過索引來檢索想要的內容:

使用索引的時候和使用store一樣,也能通過遊標(cursor)來遍歷。由於同一個索引值名下可能有好幾條資料(如果索引不是unique的話),所以這裡我們需要用到IDBKeyRange。它能根據指定的函式對結果集進行過濾。這裡,我們只想根據指定的樂隊進行檢索,所以我們用到了only()函式。也能使用其它類似於lowerBound()upperBound()bound()等函式,它們的功能也是不言自明的。

總結

可以看見,在Web應用中使用離線資料並不是十分複雜。希望通過閱讀這篇文章,各位能夠在Web應用中加入離線資料的功能,使得你們的應用更加友好易用。你可以在這裡下載所有的原始碼,嘗試一下,或者修修改改,或者用在你們的應用中。

相關文章