JavaScript是如何工作的:儲存引擎+如何選擇合適的儲存API
這是專門探索 JavaScript 及其所構建的元件的系列文章的第 16 篇。
如果你錯過了前面的章節,可以在這裡找到它們:
- JavaScript 是如何工作的:引擎,執行時和呼叫堆疊的概述!
- JavaScript 是如何工作的:深入V8引擎&編寫優化程式碼的5個技巧!
- JavaScript 是如何工作的:記憶體管理+如何處理4個常見的記憶體洩漏!
- JavaScript 是如何工作的:事件迴圈和非同步程式設計的崛起+ 5種使用 async/await 更好地編碼方式!
- JavaScript 是如何工作的:深入探索 websocket 和HTTP/2與SSE +如何選擇正確的路徑!
- JavaScript 是如何工作的:與 WebAssembly比較 及其使用場景!
- JavaScript 是如何工作的:Web Workers的構建塊+ 5個使用他們的場景!
- JavaScript 是如何工作的:Service Worker 的生命週期及使用場景!
- JavaScript 是如何工作的:Web 推送通知的機制!
- JavaScript是如何工作的:使用 MutationObserver 跟蹤 DOM 的變化!
- JavaScript是如何工作的:渲染引擎和優化其效能的技巧!
- JavaScript是如何工作的:深入網路層 + 如何優化效能和安全!
- JavaScript是如何工作的:CSS 和 JS 動畫底層原理及如何優化它們的效能!
- JavaScript的如何工作的:解析、抽象語法樹(AST)+ 提升編譯速度5個技巧!
- JavaScript是如何工作的:深入類和繼承內部原理+Babel和 TypeScript 之間轉換!
這篇主要一些內容原作者大部分是通過 MDN 整理的組合的,我也是根據中文的 MND 整理的組合。
概述
在設計 Web 應用程式時,為本地瀏覽器選擇合適的儲存機制至關重要, 一個好的儲存引擎可以確保可靠地儲存資訊,減少頻寬,提高響應能力。正確的儲存快取策略是實現離線移動 Web 體驗的核心構建塊,同時也大大的提高了使用者體驗。
在本章中,討論可選擇的儲存 Api 和服務,並提供一些在構建 Web應用程式,該使用哪種儲存引擎。
資料模型
資料儲存模型確定資料在內部的組織方式,這會影響 Web 應用程式的整個設計,合理的資料模式會讓 Web 應用程式在完成它應有的任務下還能讓執行速度更加高效。對於所有與工程相關的問題,沒有存在最好的解決方法,也沒有適用於所有問題的解決方案,不同場景下有不同的選擇。所以,來看看可選擇的資料模型:
- 結構化: 儲存在具有預定義欄位的表中的資料(這是典型的基於 SQL 的資料庫管理系統)適行靈活的動態查詢。瀏覽器中結構化資料儲存的一個代表的例子是 IndexedDB 。
- Key/Value: 鍵/值 資料儲存和相關的 NoSQL 資料庫提供了儲存和檢索由唯一鍵索引的非結構化資料的能力。 鍵/值 資料儲存類似於雜湊表,因為它們允許對索引的不透明資料進行長時間訪問。 鍵/值 資料儲存的代表例子是瀏覽器中的 Cache API 和伺服器上的 Apache Cassandra 。
Apache Cassandra 是一套開源分散式資料庫管理系統,由Facebook開發,用於儲存特別大的資料。
- 位元組流: 這個簡單的模型將資料儲存為長度不透明的位元組字串變數,將任何形式的內部組織留給應用層。這個模型特別適合於檔案系統和其他分層組織的資料塊。位元組流資料儲存的代表例子包括檔案系統和雲端儲存服務。
持久化
web 應用程式的儲存方法可以根據資料持久化的時間段進行劃分:
- 會話持久化: 該類別中的資料僅在單個 Web 會話或瀏覽器選項卡保持啟用狀態時才持久,具有會話永續性的儲存機制的一個示例是 Session Storage API 。
- 裝置的持久化: 此類別中的資料在特定裝置上跨會話和瀏覽器選項卡/視窗持久化,具有裝置持久化的儲存機制的一個示例是 Cache API 。
- 此類中的資料跨會話和裝置持久化。因此,它是最健壯的資料永續性形式。但是,它不能儲存在裝置本身上,這意味需要在某種伺服器端儲存。在這裡不會詳細討論它,因為本文的重點是在裝置本身上儲存資料。
瀏覽器中的資料持久化
現在,有相當多的瀏覽器 Api 用來儲存資料。這裡將逐一介紹其中的一些及它們的區別,以便後續我們能夠容合理的選擇使用。
然而,在選擇如何持久化資料之前,有幾件事需要考慮。當然,有必要知道的的第一件事是你的 Web 應用程式應用場景是什麼,以及以後如何迭代和豐富。即使你知道了這些,最終也會有幾個選擇。所以,以下是需要了解的:
- 瀏覽器支援 — 標準化和完善的 API 更值得我們選擇,因為它們往往壽命更長,支援更廣泛, 這些API 還享有更豐富的文件和開發人員社群。
- 事務 — 有時,相關儲存操作的集合原子地成功或失敗是很重要的。傳統上,資料庫使用事務模型支援此功能,其中相關更新可以分組到任意單元中。
- 同步/非同步 — 有些儲存 Api 是同步的,因為儲存或檢索請求會阻塞當前活動的執行緒,直到請求完成。使用同步儲存 API 會阻塞主執行緒,併為 Web 應用程式的 UI 建立凍結體驗。如果可能,使用非同步API。
比較
在本節中,瞭解決 Web 開發人員的當前可用儲存 Api,並從各個維度上進行比較。
檔案系統API
通過 FileSystem API, Web 應用就可以建立、讀取、導航使用者本地檔案系統中的沙盒部分以及向其中寫入資料。
API 被分為以下不同的主題:
- 讀取和處理檔案:File/Blob、FileList、FileReader
- 建立和寫入:BlobBuilder、FileWriter
- 目錄和檔案系統訪問:DirectoryReader、FileEntry/DirectoryEntry、LocalFileSystem
FileSystem API 是非標準 API。在釋出環境因慎重使用,因為並是所有的瀏覽器都支援,實現方式可能存在很大的不相容性,並且在將來可能也會發生變化。
請求檔案系統
網路應用可通過呼叫 window.requestFileSystem()
請求對沙盒檔案系統的訪問許可權:
// Note: The file system has been prefixed as of Google Chrome 12: window.requestFileSystem = window.requestFileSystem || window.webkitRequestFileSystem; window.requestFileSystem(type, size, successCallback, opt_errorCallback)
type:檔案儲存是否應該是持久的。可能的值包括 window.TEMPORARY
和 window.PERSISTENT
。通過 TEMPORARY 儲存的資料可由瀏覽器自行決定刪除(例如在需要更多空間的情況下),要清除PERSISTENT 儲存,必須獲得使用者或應用的明確授權,並且需要使用者向你的應用授予配額。
size:應用需要用於儲存的大小 (以位元組為單位)。
successCallback:檔案系統請求成功時呼叫的回撥,其引數為 FileSystem 物件。
opt_errorCallback:用於處理錯誤或獲取檔案系統的請求遭到拒絕時可選的回撥,其引數為 FileError 物件。
如果你是首次呼叫 requestFileSystem()
,系統會為你的應用建立新的儲存。請注意,這是沙箱檔案系統,也就是說,一個網路應用無法訪問另一個應用的檔案。
在訪問檔案系統之後,可以對檔案和目錄執行大多數標準操作。
與其他儲存型別相比,檔案系統是一個完全不同的儲存型別,因為它的旨在滿足資料庫,很不能很好地服務的客戶端儲存用例。通常,這些應用程式處理大型二進位制blob或與瀏覽器上下文之外的應用程式共享資料。
以下使用檔案系統 API 的幾個示例:
-
有上傳的應用
- 當你選擇一個檔案或目錄進行上傳時,你可以賦值檔案到一個本地沙盒並一次上傳一個塊。
- 應用可以在一次中斷後重新上傳,中斷可能包括瀏覽器被關閉或崩潰,連線中斷,或電腦被關閉。
-
視訊遊戲或其他使用大量媒體資源的應用
- 用下載一個或多個大壓縮包並在本地將他們解壓到一個檔案目錄中。
- 應用能在後臺預取資源,從而讓使用者能夠進入下一項工作或遊戲等級,而不需要等待下載。
-
音訊或照片編輯器使用線下訪問或本地快取
- 應用可以分段寫入檔案(例如只覆蓋ID3/EXIF標籤而不是整個檔案)。
-
線下視訊瀏覽
- 應用可以訪問只下載了部分的檔案。
-
線下網路郵件客戶端
- 客戶端下載附件並在本地儲存它們。
- 客戶端快取附件用於稍後的上傳。
目前瀏覽器對檔案系統 API 的支援:
Local storage
只讀的 localStorage 允許你訪問一個 Document 的遠端(origin)物件 Storage ;其儲存的資料能在跨瀏覽器會話保留。 localStorage 類似 sessionStorage ,其區別在於:儲存在 localStorage 的資料可以長期保留;而當頁面會話結束——也就是說當頁面被關閉時,儲存在 sessionStorage 的資料會被清除 。
應注意無論資料儲存在 localStorage 還是 sessionStorage , 它們都特定於頁面的協議 。
另外,localStorage 中的鍵值對總是以字串的形式儲存。
當前瀏覽器對API的支援:
Session storage
sessionStorage 屬性允許你訪問一個 session Storage 物件。它與 localStorage 相似,不同之處在於 localStorage 裡面儲存的資料沒有過期時間設定,而儲存在 sessionStorage 裡面的資料在頁面會話結束時會被清除。頁面會話在瀏覽器開啟期間一直保持,並且重新載入或恢復頁面仍會保持原來的頁面會話。 在新標籤或視窗開啟一個頁面時會在頂級瀏覽上下文中初始化一個新的會話 ,這點和 session cookies 的執行方式不同。
應該注意的是,無論是 localStorage 還是 sessionStorage 中儲存的資料 都僅限於該頁面的協議 。
當前瀏覽器對API的支援:
Cookies
HTTP Cookie(也叫Web Cookie或瀏覽器Cookie)是伺服器傳送到使用者瀏覽器並儲存在本地的一小塊資料,它會在瀏覽器下次向同一伺服器再發起請求時被攜帶併發送到伺服器上。通常,它用於告知服務端兩個請求是否來自同一瀏覽器,如保持使用者的登入狀態。Cookie 使基於 無狀態 的 HTTP 協議記錄穩定的狀態資訊成為了可能。
Cookie主要用於以下三個方面:
- 會話狀態管理(如使用者登入狀態、購物車、遊戲分數或其它需要記錄的資訊)
- 個性化設定(如使用者自定義設定、主題等)
*瀏覽器行為跟蹤(如跟蹤分析使用者行為等)
Cookie曾一度用於客戶端資料的儲存,因當時並沒有其它合適的儲存辦法而作為唯一的儲存手段,但現在隨著現代瀏覽器開始支援各種各樣的儲存方式,Cookie漸漸被淘汰。由於伺服器指定Cookie後,瀏覽器的每次請求都會攜帶Cookie資料,會帶來額外的效能開銷(尤其是在移動環境下)。
cookie型別有兩種:
- 會話 Cookie — 瀏覽器關閉之後它會被自動刪除,也就是說它僅在會話期內有效。會話期Cookie不需要指定過期時間(Expires)或者有效期(Max-Age)。需要注意的是,有些瀏覽器提供了會話恢復功能,這種情況下即使關閉了瀏覽器,會話期Cookie也會被保留下來,就好像瀏覽器從來沒有關閉一樣。
- 持久 Cookie — 和關閉瀏覽器便失效的會話期Cookie不同,永續性Cookie可以指定一個特定的過期時間(Expires)或有效期(Max-Age)。
當機器處於不安全環境時,切記不能通過HTTP Cookie儲存、傳輸敏感資訊,且所有瀏覽器都廣泛支援cookie。
Cache
Cache 介面為快取的 Request / Response 物件對提供儲存機制,例如,作為 ServiceWorker 生命週期的一部分。請注意,Cache 介面像 workers 一樣,是暴露在 window 作用域下的。儘管它被定義在 service worker 的標準中, 但是它不必一定要配合 service worker 使用.
一個域可以有多個命名 Cache 物件。你需要在你的指令碼 (例如,在 ServiceWorker 中)中處理快取更新的方式。除非明確地更新快取,否則快取將不會被更新;除非刪除,否則快取資料不會過期。使用 CacheStorage.open(cacheName) 開啟一個Cache 物件,再使用 Cache 物件的方法去處理快取.
你需要定期地清理快取條目,因為每個瀏覽器都硬性限制了一個域下快取資料的大小。快取配額使用估算值,可以使用 StorageEstimate API 獲得。瀏覽器盡其所能去管理磁碟空間,但它有可能刪除一個域下的快取資料。瀏覽器要麼自動刪除特定域的全部快取,要麼全部保留。確保按名稱安裝版本快取,並僅從可以安全操作的指令碼版本中使用快取。檢視 Deleting old caches 獲取更多資訊.
CacheStorage 介面表示 Cache 物件的儲存。
- 它提供了一個 ServiceWorker ,其它型別worker或者 window 範圍內可以訪問到的所有命名cache的主目錄(它並不是一定要和 service workers 一起使用,即使它是在 service workers 規範中定義的),並維護一份字串名稱到相應 Cache 物件的對映。
- 使用 CacheStorage.open() 獲取 Cache 例項。
- 使用 CacheStorage.match() 檢查給定的 Request 是否是 CacheStorage 物件跟蹤的任何 Cache 物件中的鍵。
你可以通過 caches 屬性訪問 CacheStorage .
IndexedDB
IndexedDB 是一種在使用者瀏覽器中持久儲存資料的方法。因為它允許你建立具有豐富查詢功能的 Web 應用程式,無論網路可用性如何,這些應用程式都可以線上和離線工作。IndexedDB 對於儲存大量資料的應用程式(例如,借出庫中的 DVD 目錄)和不需要持久 internet 連線才能工作的應用程式(例如,郵件客戶機、待辦事項列表和記事本)非常有用。
在本文中,會更詳細地討論儲存資料庫,因為其餘的儲存 Api 都是眾所周知的。另外,隨著 Web 應用程式的複雜性越來越高,IndexedDB 也越來越受歡迎。
IndexedDB的內部結構
IndexedDB 通過“鍵”來儲存和檢索物件。對資料庫所做的所有更改都發生在事務中,像大多數 Web 儲存解決方案一樣,IndexedDB 遵循同源策略。因此,雖然可以訪問域中儲存的資料,但是不能跨不同的域訪問資料。
IndexedDB 是一個 非同步 API,可以在大多數上下文中使用,包括 WebWorkers 。它過去也包括一個同步版本,供 Web 開發者使用,但是由於 Web 社群對它缺乏興趣,所以從規範中刪除了這個版本。
IndexedDB 曾經有一個與之競爭的規範,稱為 WebSQL 資料庫,但是 W3C 棄用了它。雖然 IndexedDB 和WebSQL 都是儲存解決方案,但它們提供的功能不同。WebSQL 資料庫是一個關係資料庫訪問系統,而IndexedDB 是一個索引表系統。
不要一開始就使用 IndexedDB,這依賴於你對其他型別資料庫的假設。相反,應該仔細閱讀文件,以下是一些需要牢記的基本概念:
- IndexedDB 資料庫使用 key-value 鍵值對儲存資料 — values 資料可以是結構非常複雜的物件,key可以是物件自身的屬性。你可以對物件的某個屬性建立索引(index)以實現快速查詢和列舉排序。key可以是二進位制物件。
- IndexedDB 是事務模式的資料庫 — 任何操作都發生在事務(transaction)中。 IndexedDB API提供了索引(indexes)、表(tables)、指標(cursors)等等,但是所有這些必須是依賴於某種事務的。因此,你不能在事務外執行命令或者開啟指標。事務(transaction)有生存週期,在生存週期以後使用它會報錯。並且,事務(transaction)是自動提交的,不可以手動提交。
- The IndexedDB API 基本上是非同步的 — IndexedDB 的 API 不通過 return 語句返回資料,而是需要你提供一個回撥函式來接受資料。執行 API 時,你不以同步(synchronous)方式對資料庫進行“儲存”和“讀取”操作,而是向資料庫傳送一個操作“請求”。當操作完成時,資料庫會以DOM事件的方式通知你,同時事件的型別會告訴你這個操作是否成功完成。這個過程聽起來會有些複雜,但是裡面是有明智的原因的。這個和 XMLHttpRequest 請求是類似的。
- IndexedDB資料庫“請求”無處不在 — 每一個“請求”都包含
onsuccess
和onerror
事件屬性,同時你還對 “事件” 呼叫addEventListener()
和removeEventListener()
。“請求” 還包括readyState
,result
和errorCode
屬性,用來表示“請求”的狀態。result
屬性尤其神奇,他可以根據“請求”生成的方式變成不同的東西,例如:IDBCursor 例項、剛插入資料庫的數值對應的鍵值(key)等。 - IndexedDB是面向物件的 — indexedDB 不是用二維表來表示集合的關係型資料庫,這一點非常重要,將影響你設計和建立你的應用程式。
- indexedDB 不使用結構化查詢語言(SQL) — 它通過索引(index)所產生的指標(cursor)來完成查詢操作,從而使你可以迭代遍歷到結果集合。如果你不熟悉NoSQL系統,可以參考 維基百科相關文章 。
- IndexedDB遵循同源(same-origin)策略 — “源”指指令碼所在文件URL的域名、應用層協議和埠。每一個“源”都有與其相關聯的資料庫。在同一個“源”內的所有資料庫都有唯一、可區別的名稱。
IndexedDB侷限性
以下情況不適合使用IndexedDB
- 全球多種語言混合儲存。國際化支援不好。需要自己處理。
- 和伺服器端資料庫同步。你得自己寫同步程式碼。
- 全文搜尋。IndexedDB 介面沒有類似 SQL 語句中 LIKE 的功能。
注意,在以下情況下,資料庫可能被清除:
- 使用者請求清除資料。
- 瀏覽器處於隱私模式。最後退出瀏覽器的時候,資料會被清除。
- 硬碟等儲存裝置的容量到限。
- 資料損壞。
- 進行與特性不相容的操作。
- 確切的環境和瀏覽器特性會隨著時間改變,但瀏覽器廠商通常會遵循盡最大努力保留資料的理念。
確切的環境和瀏覽器特性會隨著時間改變,但瀏覽器廠商通常會遵循盡最大努力保留資料的理念。
選擇正確的儲存API
如前所述,最好選擇儘可能多的瀏覽器廣泛支援的 Api,並提供非同步呼叫模型,以最大限度地提高 UI 響應能力。這些標準自然會導致以下技術選擇:
- 對於離線儲存,請使用 Cache API 。任何支援建立離線應用程式所需的 Service Worker technology 的瀏覽器都可以使用這個 API,Cache API 非常適合儲存與已知 URL 關聯的資源。
- 要儲存應用程式狀態和使用者生成的內容,請使用IndexedDB。這使得使用者可以在更多的瀏覽器中離線工作,而不僅僅是那些支援快取API的瀏覽器。
原文:
https://blog.sessionstack.com...