前端最能打的本地存儲方案
前言
大家好,我是林三心,用最通俗易懂的話講最難的知識點是我的座右銘,基礎是進階的前提是我的初心。
之前開發了一個離線存儲的需求,需要在本地存儲較大的數據量,並且還要考慮到多種場景下的存儲方式兼容。產品的原話就是 “要又大又全”。既然存儲量大,也要覆蓋全多種設備多種瀏覽器。
方案選擇
-
既然要存儲的數量大,得排除 cookie
-
localStorage,雖然比 cookie 多,但是同樣有上限(5M)左右,備選
-
websql 使用簡單,存儲量大,兼容性差,備選
-
indexDB api 多且繁瑣,存儲量大、高版本瀏覽器兼容性較好,備選
既然羅列了一些選擇,都沒有十全十美的,那麼有沒有一種能夠集合這多種方式的插件呢? 漸進增強 or 優雅降級 的存在
衝着這個想法,就去 github 和谷歌找了一下,還真的有這麼一個插件。
那就是 localforage github 地址 [1]
localforage
localForage 是一個 JavaScript 庫,只需要通過簡單類似 localStorage
API 的異步存儲來改進你的 Web 應用程序的離線體驗。它能存儲多種類型的數據,而不僅僅是字符串。
關於兼容性
localForage 有一個優雅降級策略,若瀏覽器不支持 IndexedDB 或 WebSQL,則使用 localStorage。在所有主流瀏覽器中都可用:Chrome,Firefox,IE 和 Safari(包括 Safari Mobile)。 下面是 indexDB、web sql、localStorage 的一個瀏覽器支持情況,可以發現,兼容性方面 loaclForage 基本上滿足 99% 需求
關於存儲量
首先 indexDB 的存儲,理論上是硬件有多大內存就可以存多少,但是有些瀏覽器廠商會限制,具體限制各家不同,但是基本最小是 250M 起步
使用
解決了兼容性和存儲量的點,我們就來看看 localforage 的基礎用法
安裝
# 通過 npm 安裝:
npm install localforage
複製代碼
// 直接引用
<script src="localforage.js"></script>
<script>console.log('localforage is: ', localforage);</script>
複製代碼
獲取存儲
getItem(key, successCallback)
從倉庫中獲取 key 對應的值並將結果提供給回調函數。如果 key 不存在,getItem()
將返回 null
。
localforage.getItem('somekey').then(function(value) {
// 當離線倉庫中的值被載入時,此處代碼運行
console.log(value);
}).catch(function(err) {
// 當出錯時,此處代碼運行
console.log(err);
});
// 回調版本:
localforage.getItem('somekey', function(err, value) {
// 當離線倉庫中的值被載入時,此處代碼運行
console.log(value);
});
複製代碼
設置存儲
setItem(key, value, successCallback)
將數據保存到離線倉庫。你可以存儲如下類型的 JavaScript 對象:
-
Array
-
ArrayBuffer
-
Blob
-
Float32Array
-
Float64Array
-
Int8Array
-
Int16Array
-
Int32Array
-
Number
-
Object
-
Uint8Array
-
Uint8ClampedArray
-
Uint16Array
-
Uint32Array
-
String
localforage
.setItem("somekey", "some value")
.then(function (value) {
// 當值被存儲後,可執行其他操作
console.log(value);
})
.catch(function (err) {
// 當出錯時,此處代碼運行
console.log(err);
});
// 不同於 localStorage,你可以存儲非字符串類型
localforage
.setItem("my array", [1, 2, "three"])
.then(function (value) {
// 如下輸出 `1`
console.log(value[0]);
})
.catch(function (err) {
// 當出錯時,此處代碼運行
console.log(err);
});
// 你甚至可以存儲 AJAX 響應返回的二進制數據
req = new XMLHttpRequest();
req.open("GET", "/photo.jpg", true);
req.responseType = "arraybuffer";
req.addEventListener("readystatechange", function () {
if (req.readyState === 4) {
// readyState 完成
localforage
.setItem("photo", req.response)
.then(function (image) {
// 如下爲一個合法的 <img> 標籤的 blob URI
var blob = new Blob([image]);
var imageURI = window.URL.createObjectURL(blob);
})
.catch(function (err) {
// 當出錯時,此處代碼運行
console.log(err);
});
}
});
複製代碼
刪除存儲
removeItem(key, successCallback)
從離線倉庫中刪除 key 對應的值。
localforage.removeItem('somekey').then(function() {
// 當值被移除後,此處代碼運行
console.log('Key is cleared!');
}).catch(function(err) {
// 當出錯時,此處代碼運行
console.log(err);
});
複製代碼
清空存儲
clear(successCallback)
從數據庫中刪除所有的 key,重置數據庫。
localforage.clear()
將會刪除離線倉庫中的所有值。謹慎使用此方法。
localforage.clear().then(function() {
// 當數據庫被全部刪除後,此處代碼運行
console.log('Database is now empty.');
}).catch(function(err) {
// 當出錯時,此處代碼運行
console.log(err);
});
複製代碼
更多
除了基本的增刪查改,還有一些配置,如指定具體使用哪一種存儲方式、設置數據庫的名稱、長度等信息 可參考 官方文檔 [2]
localforage 是否萬事大吉?
用上了 localforage 一開始我也以爲可以完全滿足萬惡的產品了,然而。。。 翻車了
問題
在這個功能上線半年,一直相安無事,有一天晚上突然產品說接到反饋說有用戶的手機進入頁面沒有緩存上次的操作數據。
我第一反應,“不可能,絕對不可能”
如下圖:
這玩意,一些小年輕都可能沒見過。。。。 iphone4 哇,現在是出到了 iphone14 了吧???
不得了不得了,iphone4 居然也是我們的用戶羣體???
分析
既然遇上了,還是冷靜分析一下吧。起初第一反應是這古董機的兼容性有問題,是不是隻支持 localstorage 導致只能存儲 5M 的內容,超過了上限,導致無法緩存了?
然而,當產品不知道從哪找到了一部 iphone4 給我(我也真的服了這個老 6),我拿到真機試了下,得到讓我無法呼吸的結果,iphone4 這古董機居然支持 indexDB,那麼就不是超過了 5M 的上限導致緩存失敗了
進一步假設
在知道 iphone4 居然支持 indexDB 後,我失去頭緒了,拿着十年前的這個古董機,隨便翻翻,看看系統,看看版本,沒看出什麼問題,但是我發現這 iphone4 的內存也是出奇的小,只有 8G 內存。等等,8G 內存,如果手機內存不足的前提下,localforage 繼續緩存會怎麼樣?
隨即,隨便下載點軟件,毫不費力就將這臺 iphone4 的內存整得只剩下 50M 不到了,手機已經開始提示要清理內存。
在這種狀態下,嘗試使用 localforage,不出意外,拋錯了 QuotaExceededError
的 DOMError
延伸
雖然現在的硬件設備內存大部分都很大,但是本着產品的 “又大又全” 理念,還是打算處理一下。當然除了處理這臺古董機,也延伸出更多優化的可能性
-
當設備不支持 indexDB 和 web sql 的時候,只支持 loaclStorage 存儲量只有 5M,應該怎麼處理?
-
如果存儲數據出現了髒數據或者讀取問題,想要清理用戶設備上的數據怎麼處理?
解決
存儲數據的時候加上存儲的時間戳和模塊標識,加時間戳一起存儲
setItem({
value: '1',
label: 'a',
module: 'a',
timestamp: '11111111111'
})
複製代碼
-
如果是遇到存儲使用報錯的情況,try/catch 捕獲之後,通過判斷報錯提示,去執行相應的操作,遇到內存不足的情況,則根據時間戳和模塊標識清理一部分舊數據(內存不足的情況還是比較少的)
-
在用戶手機上產生髒數據的情況,想要清理的這種情況的 處理方式是:
-
讓後端在用戶信息接口裏面加上緩存有效期時間戳,當該時間戳存在,則前端會進行一次對本地存儲掃描
-
在有效期時間戳之前的數據,結合模塊標識,進行清理,清理完畢後調用後端接口上報清理日誌
-
模塊標識的意義是清理數據的時候,可以按照模塊去清理(選填)
結語
我是林三心,一個熱心的前端菜鳥程序員。如果你上進,喜歡前端,想學習前端,那咱們可以交朋友,一起摸魚哈哈,摸魚羣,關注我,拉你進羣,有 5000 多名前端小夥伴在等着一起學習哦 -->
作者:趴窩熊貓
鏈接:https://juejin.cn/post/7199826518569779256
來源:稀土掘金
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/GXZOc82nIGtNL6jIZHWeiw