创建一个storageevent事件_谈谈StorageEvent
編者按:本文作者 劉觀宇,360 奇舞團高級前端工程師、技術(shù)經(jīng)理,W3C CSS 工作組成員。
紛紛紅紫已成塵,布谷聲中夏令新。夾路桑麻行不盡,始知身是太平人。 ——宋.陸游 《初夏絕句》
我們在開發(fā)多Tab應(yīng)用時候,常常會遇到多個Tab狀態(tài)同步的問題。
想象如下場景:用戶主界面,顯示用戶購物車內(nèi)待結(jié)算的商品總數(shù)。此時,用戶可能打開多個Tab。當用戶添加新商品到購物車的時候,需要更新購物車的數(shù)量。
此時,當前頁面需要向服務(wù)器發(fā)起請求,在得到添加成功響應(yīng)的時候,可以更新用戶界面。為了兼顧體驗和可靠性,如果確信添加成功概率比較高的時候,也可以先更新界面,當多數(shù)返回錯誤的時候,可以給用戶界面做狀態(tài)回滾。為了下次展示方便,我們還會把這個數(shù)據(jù)寫到LocalStorage里面。用戶再次打開時候,可以優(yōu)先從localStorage中取值。
當前頁面解決了,那么如果同時打開多個Tab該如何解決呢?這里使用StorageEvent可能是一種代價較小的解決方案。
StorageEvent是什么呢?
是一種Event,可以通過標準的Event監(jiān)聽器操作。
當storage變化時候,事件會被派發(fā)到所有同域下的其他頁面。
觸發(fā)變化的當前頁面,沒有事件派發(fā)。
這里有一個簡單的示例可以展示這個API的用法。
const?STORAGE_KEY?=?"cartlist"
const?getStorage?=?()?=>?{
????try?{
????????let?rets?=?window.localStorage.getItem(STORAGE_KEY)
????????if?(rets?===?null)?{
????????????return?[]
????????}
????????return?JSON.parse(rets)
????}
????catch(e){
????????return?[]
????}
}
const?addCart?=?(value)?=>?{
????let?rets?=?getStorage()
????rets.push(value)
????window.localStorage.setItem(STORAGE_KEY,?JSON.stringify(rets))
????return?rets
}
const?minusCart?=?(value)?=>?{
????let?rets?=?getStorage()
????let?idx?=?rets.indexOf(value)
????if?(idx?!==?-1){
????????rets.splice(idx,?1)
????????window.localStorage.setItem(STORAGE_KEY,?JSON.stringify(rets))
????}
????return?rets
}
const?render?=?()?=>?{
????let?rets?=?getStorage()
????if?(rets.length){
????????$("#num").html(rets.length).show()
????}
????else?{
????????$("#num").hide()
????}
????$(".list li").each((i,el)?=>?{
????????if?(rets.includes(i)){
????????????$(el).find("a:nth-child(1)").css("visibility",?"hidden")
????????????$(el).find("a:nth-child(2)").css("visibility",?"visible")
????????}
????????else?{
????????????$(el).find("a:nth-child(1)").css("visibility",?"visible")
????????????$(el).find("a:nth-child(2)").css("visibility",?"hidden")
????????}
????})
}
$(".list a").on("click",?(e)=>?{
????let?opIdx?=?$(e.target).parent().find("a").index(e.target)
????let?line?=?$(e.target).parent().parent()
????let?idx?=?$(".list li").index(line)
????opIdx?===?0???addCart(idx)?:?minusCart(idx)
????render()
????return?false
})
window.addEventListener('storage',?(e)?=>?{
????render()
})
render()
其中,下面這行代碼是實現(xiàn)的關(guān)鍵:
window.addEventListener('storage',?(e)?=>?{
????render()
})
當我們注釋掉這個語句,我們的頁面同步就不能運行了。
讀者可以打開多個Tab并觀察頁面的變化 https://jsbin.com/radekilosu/1/edit?html,css,js,output 。
實際上,這個事件e上還帶有很多信息,方便編程時,對于事件做精確的控制。
| key | 發(fā)生變化的storageKey |
| newValue | 變換后新值 |
| oldValue | 變換前原值 |
| storageArea | 相關(guān)的變化對象 |
| url | 觸發(fā)變化的URL,如果是frameset內(nèi),則是觸發(fā)幀的URL |
上述各值都是只讀的。
還有一點沒有解決掉,就是觸發(fā)storage變化的本頁面,不能接收這個值,這個一般情況下是沒問題。當然,為了一致性,我們可以自行new一個事件,在發(fā)生時候主動觸發(fā)它。
此時我們可以包裝一個新的Storage對象:
var?Storage?=?{
????setItem?:?function(k,v){
??????localStorage.setItem(k,v);
????},
????removeItem?:?function(k){
??????localStorage.removeItem(k);
????},
????clear:?function?(){},
????getItem:?function(k)
}
此時,我們再包裝一個函數(shù):
function?dispatchMe(key,?oldval,?newval,?url,?storage){
????var?se?=?document.createEvent("StorageEvent");
????se.initStorageEvent('storage',?false,?false,?key,?oldval,?newval,?url,?storage);
????window.dispatchEvent(se);
}
此時,我們只需要再setItem、removeItem、clear中獲取對應(yīng)的值,并手動調(diào)用一dispatchMe,同時把和localStorage打交道的地方改為調(diào)用我們的新對象即可。
參考資料
https://developer.mozilla.org/en-US/docs/Web/API/Window/storage_event
https://www.cnblogs.com/cczw/p/3196195.html
關(guān)于奇舞周刊
《奇舞周刊》是360公司專業(yè)前端團隊「奇舞團」運營的前端技術(shù)社區(qū)。關(guān)注公眾號后,直接發(fā)送鏈接到后臺即可給我們投稿。
總結(jié)
以上是生活随笔為你收集整理的创建一个storageevent事件_谈谈StorageEvent的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 用友 无法正确解析服务器,用友T3软件登
- 下一篇: java题霸_牛客题霸每日一题 + NC