localStorage 存滿了怎麼辦?

先來幾道面試題

一、a.meituan.com 和 b.meituan.com 這兩個域可以共享同一個 localStorage 嗎?javascript

二、在 webview 中打開一個頁面:i.meituan.com/home.html,點擊一個按鈕,調用 js 橋打開一個新的 webview:i.meituan.com/list.html,這兩個分屬不一樣 webview 的頁面能共享同一個 localStorage 嗎?css

三、若是 localStorage 存滿了,再往裏存東西,或者要存的東西超過了剩餘容量,會發生什麼?html

答案前端

一、同一個域名(document.domain)共享同一個 localStorage,a.meituan.com 和 b.meituan.com 是兩個域名,因此不能共享java

二、能。至關於同一個瀏覽器的不一樣標籤頁。不一樣瀏覽器之間不能共享。ios

三、存不進去並報錯(QuotaExceededError)web

 

技術很簡單,業務很麻煩

在大公司,同一個域名下可能存在幾十上百條業務線,每條業務線均可能由於各類理由往 localStorage 裏塞東西,跨頁面傳數據啦、緩存啦、離線化啦、性能優化啦...,5M 看起來不少,其實很快就用完了。而開發時基本無感知,是由於你們都只訪問本身的業務,但用戶會訪問各類業務,時間一久,很容易就存滿了,凡是嚴重依賴 localStorage 的業務流程都存在風險,寫可能出問題,讀天然也會出問題。面試

一種容易想到的方案是,當 localStorage 存滿後降級到 sessionStorage 裏。看上去沒啥問題,但實際業務中 app 內 h5 頁面跳轉經常採用新打開 webview 的方式,這麼作的好處是關閉一個 webview 能夠直接回到上一個頁面,而不用從新加載頁面,對於訂單填寫這類帶有狀態的頁面就很須要這麼作。新打開 webview 等於新打開一個會話,而 sessionStorage 只能存在於同一個會話中,所以 sessionStorage 沒法跨頁面共享。後端

那降級到 cookie 裏呢?cookie 一共才 50 個,總大小不超過 4k,做爲 backup 過於脆弱,並且還會影響請求的效率。若是後端對請求頭大小作了限制,還可能產生 413 錯誤,致使請求被攔截。瀏覽器

那降級到 url 上呢?很麻煩。好比有一個交互流程是這樣的:頁面 A => 頁面 B => 頁面 C,若是頁面 A 的數據要傳到頁面 C,就得經過頁面 B 作一層中轉。並且 url 長度也是有限制的。

單頁應用解決跨頁面傳數據就很簡單,改形成單頁應用呢?這個就得估算成本,看老闆們認不承認了,並且原有應用積累了大量的業務邏輯,沒有註釋,沒有測試用例,需求文檔散落在不知名的角落,你真能保證從新作的和原來的功能如出一轍嗎。

咱們還能夠求助客戶端同窗,經過 js bridge 提供一個仿 localStorage 的東西,不過要考慮版本的問題,新版 app 裏使用了客戶端提供的 store,怎麼兼顧老版 app,並且還要考慮兼容瀏覽器、微信。因此這種方案也只能解決一部分問題,固然,若是 h5 的流量絕大多數都在 app 裏,那麼這種方案是能夠解決一大部分問題的,不過客戶端提供的存儲可不見得比原生的存儲可靠,仍是得加 backup。

咱們還能夠求助後端同窗,多加幾個字段甚至多加幾個接口,不過這涉及到核心業務流程的改造,風險不小,並且不見得能徹底解決問題,也沒法永久的解決問題。

咱們還能夠來一招互相傷害大法,那就是把別人存的東西都刪掉。。。

localStorage 是個好東西,不用,這是因噎廢食,用,又很難統一和約束各業務線的用法,一旦放開用,就總會面臨存滿的風險。跟你在同一個域名下作開發的人可能跟你不在同一棟樓,甚至可能不在同一個城市,你有那個影響力去統一全部人的使用規範嗎。

還有一個很討厭的事情:safari 在隱私模式下不支持 localStorage 的存取(ios11 如下),這種狀況比較罕見,但若是出了客訴,也是個大坑。

 

問題的本質

localStorage 歸根結底就兩個做用:持久化存儲與跨頁面傳數據。持久化存儲不會出問題,存不進去就存不進去唄,取不出來就去其它地方取,或者不取。問題就出在跨頁面傳數據上,上一個頁面由於 localStorage 存滿致使數據沒有寫入,下一個頁面讀取數據爲空,從而致使錯誤。

 

問題的根源

同一個域名共享同一個 localStorage,而同一個域名下存在過多獨立的業務線,業務線之間各自爲政,毫無節制的攫取公共資源,這就是 localStorage 溢出問題的根源。

就我觀察的狀況來看,不少公司都喜歡把 h5 頁面掛在 i.xxx.com 或 m.xxx.com 下,而後經過路徑劃分業務,好比 i.xxx.com/project-a, i.xxx.com/project-b...,隨着業務發展,愈來愈多的業務都加到 i.xxx.com 中,「公地悲劇」就迫不得已的產生了,並且積重難返。我之前在的團隊也是如此,用 h五、js、css 這樣的類型名稱來劃分目錄,初期東西少,天然沒問題,但後來全部應用都把資源塞到 js 文件夾、css 文件夾下,一個文件夾包含了來自五湖四海的上百個文件,維護起來十分難受。

經過應用類型劃分,而不是經過業務類型劃分,這是最初架構策略的問題。若是 a 業務掛在 a.xxx.com 下,b 業務掛在 b.xxx.com 下,每一個業務有獨立的團隊維護,localStorage 從公共資源變成團隊的私有財產,或許這樣才能從根源上解決 localStorage 無限膨脹的問題。有網友提出對 i.xxx.com 進一步劃分子域,其實也是這個思路。

 

理想的方案

假設咱們回到起點,從零建設前端工程,咱們怎麼避免 localStorage 存滿的問題?

一、劃分域名。各域名下的存儲空間由各業務組統一規劃使用

二、跨頁面傳數據:考慮單頁應用、優先採用 url 傳數據

三、最後的兜底方案:清掉別人的存儲

 

互相傷害實際上是個好辦法

在已然發展好久的業務中,咱們怎麼解決此問題呢?

const QUOTA_EXCEEDED_ERR_CODE = 22
function write (key, data) {
    try {
        localStorage.setItem(key, data);
    } catch (e) {
        if (e.code === QUOTA_EXCEEDED_ERR_CODE) {
            localStorage.clear();
            localStorage.setItem(key, data);
        }
    }
}

上面這個方法仍是有點問題,由於它把本身業務要用的東西也給刪了,因此本身的業務最好統一在 key 上加一個前綴,清空 localStorage 時只刪別人的。

有的同窗可能會擔憂,這樣會不會對其它業務形成傷害?或者產生一些難以發現的 bug。其實這種擔憂很大程度上是由於忽略了實際的使用場景。用戶用同一個設備打開同一個 app,在同一個時間只能訪問一個業務,所以不會存在某個業務正在使用過程當中,localStorage 被其它業務清掉的場景,除非!除非有交叉的業務場景。

沒有銀彈,沒有十全十美的方案。

 

補充

掘金網友@FE 提出用 indexedDB 存文件類型的數據,localStorage 存業務數據,這是一種能夠緩解問題的方案。

相關文章
相關標籤/搜索