JavaScript >> Javascript 文檔 >  >> JavaScript

為本地存儲辯護

本週早些時候,Chris Heilmann 寫了一篇博文,題為 LocalStorage 沒有簡單的解決方案 1 他在其中譴責 localStorage 一樣慢,並鼓勵大家停止使用它。令人驚訝的是,在一篇關於性能的帖子中,沒有提到“緩慢”或“糟糕的性能”的實際含義。不能在真空中討論性能,這也是我對他的帖子的反應比其他任何事情都更混亂的部分原因。

什麼是慢?

localStorage 也是如此 有性能問題?很簡單,我不知道。正在從 localStorage 存儲和檢索數據 比普通的內存對象慢?是的。我為 2011 年性能來臨日曆寫了一篇關於此的帖子 2 .實際上,讀取數據時速度要慢一些。我的結論是,您應該嘗試通過將多條數據存儲在同一個鍵中來限制讀取。但與大多數性能指標一樣,這僅在您連續多次執行相同操作時才重要。如果您只讀取一個值或寫入一個值,那麼無論數據大小或系統發生什麼情況,您都可能永遠不會遇到性能問題。

所以 localStorage 比使用內存對象慢。汽車比飛機慢。這告訴我們什麼?不是很多。

痛點

事實是 localStorage 從磁盤讀取和寫入磁盤,這總是比內存操作慢,因為涉及到硬件。這是第一個問題。第二個問題是 localStorage 的 per-origin 特性 .此特性意味著打開到同一來源的兩個瀏覽器窗口或選項卡都可以讀取或寫入同一 localStorage 同時。反過來,這意味著瀏覽器需要非常聰明地了解它如何執行每個操作。如果選項卡 A 正在寫入 localStorage 大約在標籤 B 從 localStorage 讀取的同時 ,應該先執行哪個操作?

每次操作,讀寫,都需要加鎖localStorage 以確保數據完整性。這其實是個大問題。與文件交互也很冒險,因為另一個系統進程也可能在處理同一個文件。考慮一個簡單的寫操作:

localStorage.setItem("foo", "bar");

此操作是同步的,這意味著 UI 線程在瀏覽器中被阻塞,直到寫入完成。不再執行 JavaScript,也不再繪製 UI 更新。在這個方法調用期間,會發生幾件事:

  1. 如果 localStorage 正忙於另一個窗口或選項卡,然後等待。這是有問題的,因為無法知道這會持續多長時間。
  2. 如果文件忙,則等待。這些文件可能會被防病毒軟件掃描、包含在備份作業等中,因此在此類操作完成之前可能不可用。這也是有問題的,因為它是不可預測的。
  3. 打開文件進行寫入。
  4. 尋找寫入文件的正確位置。
  5. 寫入文件。
  6. 關閉文件。

只有在所有這些完成之後,瀏覽器才能繼續執行其他語句。所以真的,問題不在於 localStorage 慢,就是那個localStorage 必須對每個操作進行阻塞以保持數據完整性。

與cookies比較

與 JavaScript 最接近的可比 API 是 cookie API(雖然調用 document.cookie API 非常慷慨)。 Cookie 也是名稱-值對,儘管有一些額外的元數據,它使用文件作為存儲,並且必須在瀏覽器窗口和選項卡之間同步。我很驚訝 Chris 沒有比較 localStorage 到 cookie,因為 API 顯然是為了讓我們從將僅客戶端數據存儲在 cookie 中轉移到將其存儲在 localStorage 中 . localStorage 並非偶然 API 看起來很像各種 cookie API。

當我創建基準 3 測試 localStorage 針對用於讀寫的 cookie,結果非常有趣。 Internet Explorer、Chrome 和 Safari(包括 iOS),讀取 cookie 比從 localStorage 讀取要慢 並且寫入 cookie 比寫入 localStorage 慢得多 . Firefox 和 Opera 在寫入方面表現出與其他人相同的性能特徵(cookie 速度較慢),但從 cookie 中讀取速度更快。因此,在許多跨瀏覽器的情況下,localStorage 實際上是性能提升 在 JavaScript 中使用 cookie。

API

原因localStorage 受歡迎的部分原因是它的簡單性。這種簡單性是設計使然,最初是由瀏覽器供應商設計和實現的,這就是為什麼瀏覽器供應商現在帶頭反對它參與創建的 API 似乎很奇怪。是的,人類創造瀏覽器,人類也會犯錯,但我不認為 localStorage 的設計 是個錯誤。

當我閱讀 Chris 尋找替代方案的請求時,我的工程師大腦不斷對自己重複:“這是一個實現問題,而不是接口問題”。 Firefox 選擇預加載 localStorage 數據以提高以後的讀取性能,但這是一個實現問題。同樣,同步讀取和寫入的需求是一個實現問題——許多人忘記了 Internet Explorer 8 的 localStorage 實現 實際上是異步編寫的。那是特定於實現的細節。為什麼不讓所有的寫入異步發生,只在內存中保留一份數據的副本,這樣無論寫入狀態如何,它都能被正確讀取?

我並不是說這一定是一個容易解決的問題。我的意思是,API 設計對開發人員來說效果很好,因此有必要查看實現細節,以確定是否有一種有效的方法可以向 Web 開發人員隱藏系統的缺陷。

提議的替代方案 IndexedDB 可能是我見過的最糟糕的 API 設計之一。讀取和寫入一條數據需要太多的代碼行,以確保大多數開發人員不會使用 API,直到有人提出一個庫來抽像出可怕的東西。我理解提供這種低級異步 API 背後的基本原理(我什至參與了 Mozilla 圍繞 Web 數據庫進行的討論),但這絕對讓瀏覽器開發人員創建一個易於實現的 API 而不是創建一個易於實現的 API易於食用。這與如何製作好的 API 正好相反。 IndexedDB 永遠不會替代 localStorage ,對於大多數用途來說太複雜了。

非阻塞本地存儲

如前所述,真正的問題是 localStorage 讀寫阻塞,並且無法提前確定阻塞的時間量。如果這對您來說是一個問題(當然是在基準測試之後),那麼解決方案是使用非阻塞 localStorage  機制。如今,當您聽到“非阻塞”一詞時,您應該立即想到 Web Worker。

在不久的將來,我相信執行 I/O 的客戶端架構應該在 Web Workers 中執行所有 I/O。這意味著您的所有 localStorage 、XMLHttpRequest、Web Socket 等,I/O 應該在 worker 內部完成。基本上,你應該可以做這樣的事情:

var worker = new Worker("io.js"); 

worker.postMessage({ 
    type: "write", 
    src: "localStorage", 
    key: "foo", 
    value: "bar"  
}); 

worker.postMessage({ 
    type: "read", 
    src: "localStorage", 
    key: "foo" 
}); 

worker.onmessage = function(event) { 
    alert(event.data.value); 
};

所有的讀取和寫入都將在 UI 線程之外完成,因此阻塞並不重要。我知道我並不孤單認為這是未來的發展方向,因為 IndexedDB 規範有一整節介紹了工人中可用的同步 API 5 .為 IndexedDB 提供同步 API 使其處理起來不那麼可怕,但您需要在工作線程中使用它們。這還沒有被所有瀏覽器實現,但應該很快就會實現。再加上共享工作器的概念,網絡工作器在具有相同來源的頁面的所有選項卡之間共享,您就有了解決大量 I/O 問題的好方法。

工作人員當前可以訪問 XMLHttpRequest 、Web Sockets、文件閱讀器等等……但無法訪問 localStorage .為什麼?這確實是問題的解決方案:不要丟棄一個好的 API,因為在某些情況下它會導致問題。相反,讓它在工作線程中可用,以便我們可以選擇將讀取/寫入移出 UI 線程。

注意:有可能是跨域的localStorage 我之前寫過的方法 [6] 可能會提供一些非阻塞的好處。跨框架 postMessage() API 是異步的,但我還沒有找到一個好方法來測試如果來自同一域的 iframe 訪問 localStorage,包含頁面是否會凍結 .

結論

要求 web 開發者放棄 localStorage 很荒謬。 API有問題嗎?是的,確實有。他們是否糟糕到完全放棄使用它?絕對不。性能糟糕的說法尚未得到證實。儘管瀏覽器開發人員抱怨技術困難,但 localStorage 沒有好的替代品 .我們總是可以重新使用 cookie,但正如之前的基準測試所示,這並不一定能保證更好的性能。而且 IndexedDB 是一個初學者,因為它的 API 對於大多數用例來說太複雜了。

所以對於 Mozilla 和其他瀏覽器供應商來說,你是自己成功的受害者。您想創建一個 API 來代替 cookie 來存儲客戶端數據,並且您創建了一些很棒的東西。該界面對 Web 開發人員很友好,這就是它得到如此迅速採用的原因。你們都是非常聰明、有能力的人,我相信你們能想出比我們今天更好的方法來實現 API。另外,製作 localStorage 請在 Web Workers 中訪問。

更新(2012 年 3 月 8 日): 修正了拼寫錯誤並添加了共享工作人員參考。

參考

  1. Chris Heilmann 的 localStorage 沒有簡單的解決方案
  2. Nicholas C. Zakas 的 localStorage 讀取性能
  3. Nicholas C. Zakas 的 localStorage 與 cookie
  4. MSDN 的 Web 存儲簡介
  5. 索引數據庫 - 同步 API
  6. 向 XAuth 學習:Nicholas C. Zakas 的跨域本地存儲

Tutorial JavaScript 教程
  1. 使用 Auth0 對 Firebase 和 Angular 進行身份驗證:第 1 部分

  2. 檢查按鈕是否以特定順序 JS 被點擊

  3. 如何在 JavaScript 中使用五位長的 Unicode 字符

  4. 如何將數組的元素與具有相同索引的另一個數組中的元素相乘?

  5. 代理的樂趣

  6. 如何在 JavaScript 中實現鍊錶

  7. Axios 中的 GET、POST、PUT、DELETE

  1. 如何使用moment.js獲取年、月和日的2個日期之間的差異

  2. 構建你自己的 FROGGER 遊戲 - 超級簡單的原版 JavaScript 演練

  3. 控制台說明🔍

  4. 通過 React 生態系統的 React 初學者路線圖

  5. tsParticles v1.17.0 發布

  6. 使用 Puppeteer 學習端到端測試

  7. 如何停止傳播綁定函數而不是整個事件?

  1. 鍊式 AJAX 選擇

  2. 使用 Vue.js 創建電子商務前端 |第 3 部分:願望清單功能

  3. 進入 JavaScript 和克服挫折

  4. JavaScript 類屬性