<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          面試官: 如何讓localStorage支持過(guò)期時(shí)間設(shè)置?

          共 3951字,需瀏覽 8分鐘

           ·

          2021-12-16 20:23


          聊到?localStorage?想必熟悉前端的朋友都不會(huì)陌生, 我們可以使用它提供的?getItem,?setItem,?removeItem,?clear?這幾個(gè)?API?輕松的對(duì)存儲(chǔ)在瀏覽器本地的數(shù)據(jù)進(jìn)行讀,寫, 刪操作, 但是相比于?cookie,?localStorage?唯一美中不足的就是不能設(shè)置每一個(gè)鍵的過(guò)期時(shí)間

          localStorage 屬性允許我們?cè)L問(wèn)一個(gè) Document 源(origin)的對(duì)象 Storage;存儲(chǔ)的數(shù)據(jù)將保存在瀏覽器會(huì)話中。localStorage 類似 sessionStorage,但其區(qū)別在于:存儲(chǔ)在 localStorage 的數(shù)據(jù)可以長(zhǎng)期保留;而當(dāng)頁(yè)面會(huì)話結(jié)束——也就是說(shuō),當(dāng)頁(yè)面被關(guān)閉時(shí),存儲(chǔ)在 sessionStorage 的數(shù)據(jù)會(huì)被清除 。

          我們還應(yīng)注意,localStorage?中的鍵值對(duì)總是以字符串的形式存儲(chǔ)。

          問(wèn)題描述

          在實(shí)際的應(yīng)用場(chǎng)景中, 我們往往需要讓?localStorage?設(shè)置的某個(gè)?key?能在指定時(shí)間內(nèi)自動(dòng)失效, 所以基于這種場(chǎng)景, 我們?nèi)绾稳ソ鉀Q呢?

          1. 初級(jí)解法

          對(duì)于剛熟悉前端的朋友, 可能會(huì)立馬給出答案:

          localStorage.setItem('dooring',?'1.0.0')
          //?設(shè)置一小時(shí)的有效期
          const?expire?=?1000?*?60?*?60;
          setTimeout(()?=>?{
          ??localStorage.setItem('dooring',?'')
          },?expire)

          當(dāng)然這種方案能解決一時(shí)的問(wèn)題, 但是如果要設(shè)置任意鍵的有效期, 使用這種方案就需要編寫多個(gè)定時(shí)器,?維護(hù)成本極高, 且不利于工程化復(fù)用

          2. 中級(jí)解法

          前端工程師在有一定的工作經(jīng)驗(yàn)之后, 往往會(huì)去考慮工程化和復(fù)用性的問(wèn)題, 并對(duì)數(shù)據(jù)結(jié)構(gòu)有了一定的了解, 所以可能會(huì)有接下來(lái)的解法:

          1. localStorage存一份{key(鍵): expire(過(guò)期時(shí)間)}的映射表
          2. 重寫localStorage API, 對(duì)方法進(jìn)行二次封裝

          類似的代碼如下:

          const?store?=?{
          ??//?存儲(chǔ)過(guò)期時(shí)間映射
          ??setExpireMap:?(key,?expire)?=>?{
          ????const?expireMap?=?localStorage.getItem('EXPIRE_MAP')?||?"{}"
          ????localStorage.setItem(
          ??????'EXPIRE_MAP',?
          ??????JSON.stringify({
          ??????...JSON.parse(expireMap),
          ??????key:?expire
          ????}))
          ??},
          ??setItem:?(key,?value,?expire)?=>?{
          ????store.setExpireMap(key,?expire)
          ????localStorage.setItem(key,?value)
          ??},
          ??getItem:?(key)?=>?{
          ????//?在取值之前先判斷是否過(guò)期
          ????const?expireMap?=?JSON.parse(
          ??????localStorage.getItem('EXPIRE_MAP')?||?"{}"
          ????)
          ????if(expireMap[key]?&&?expireMap[key]?Date.now())?{
          ??????return?localStorage.getItem(key)
          ????}else?{
          ??????localStorage.removeItem(key)
          ??????return?null
          ????}
          ??}
          ??//?...
          }

          眨眼一看這個(gè)方案確實(shí)解決了復(fù)用性的問(wèn)題, 并且不同團(tuán)隊(duì)都可以使用這個(gè)方案, 但仍然有一些缺點(diǎn):

          • 對(duì)?store?操作時(shí)需要維護(hù)2份數(shù)據(jù), 并且占用緩存空間
          • 如果?EXPIRE_MAP?誤刪除將會(huì)導(dǎo)致所有過(guò)期時(shí)間失效
          • 對(duì)操作過(guò)程缺少更靈活的控制(比如操作狀態(tài), 操作回調(diào)等)

          3. 高級(jí)解法

          為了減少維護(hù)成本和空間占用, 并支持一定的靈活控制和容錯(cuò)能力, 我們又應(yīng)該怎么做呢?

          這里筆者想到了兩種類似的方案:

          1. 將過(guò)期時(shí)間存到?key?中, 如 dooring|6000, 每次取值時(shí)通過(guò)分隔符“|”來(lái)將?key?和?expire?取出, 進(jìn)行判斷

          2. 將過(guò)期時(shí)間存到?value?中, 如 1.0.0|6000, 剩下的同1

          為了更具有封裝性和可靠性, 我們還可以配置不同狀態(tài)下的回調(diào), 簡(jiǎn)單實(shí)現(xiàn)如下:

          const?store?=?{
          ??preId:?'xi-',
          ??timeSign:?'|-door-|',
          ??status:?{
          ????SUCCESS:?0,
          ????FAILURE:?1,
          ????OVERFLOW:?2,
          ????TIMEOUT:?3,
          ??},
          ??storage:?localStorage?||?window.localStorage,
          ??getKey:?function?(key:?string)?{
          ????return?this.preId?+?key;
          ??},
          ??set:?function?(
          ????key:?string,
          ????value:?string?|?number,
          ????time?:?Date?&?number,
          ????cb?:?(status:?number,?key:?string,?value:?string?|?number
          )?=>?void,
          ??)?
          {
          ????let?_status?=?this.status.SUCCESS,
          ??????_key?=?this.getKey(key),
          ??????_time;
          ????//?設(shè)置失效時(shí)間,未設(shè)置時(shí)間默認(rèn)為一個(gè)月
          ????try?{
          ??????_time?=?time
          ??????????new?Date(time).getTime()?||?time.getTime()
          ????????:?new?Date().getTime()?+?1000?*?60?*?60?*?24?*?31;
          ????}?catch?(e)?{
          ??????_time?=?new?Date().getTime()?+?1000?*?60?*?60?*?24?*?31;
          ????}
          ????try?{
          ??????this.storage.setItem(_key,?_time?+?this.timeSign?+?value);
          ????}?catch?(e)?{
          ??????_status?=?this.status.OVERFLOW;
          ????}
          ????cb?&&?cb.call(this,?_status,?_key,?value);
          ??},
          ??get:?function?(
          ????key:?string,
          ????cb?:?(status:?number,?value:?string?|?number?|?null
          )?=>?void,
          ??)?
          {
          ????let?status?=?this.status.SUCCESS,
          ??????_key?=?this.getKey(key),
          ??????value?=?null,
          ??????timeSignLen?=?this.timeSign.length,
          ??????that?=?this,
          ??????index,
          ??????time,
          ??????result;
          ????try?{
          ??????value?=?that.storage.getItem(_key);
          ????}?catch?(e)?{
          ??????result?=?{
          ????????status:?that.status.FAILURE,
          ????????value:?null,
          ??????};
          ??????cb?&&?cb.call(this,?result.status,?result.value);
          ??????return?result;
          ????}
          ????if?(value)?{
          ??????index?=?value.indexOf(that.timeSign);
          ??????time?=?+value.slice(0,?index);
          ??????if?(time?>?new?Date().getTime()?||?time?==?0)?{
          ????????value?=?value.slice(index?+?timeSignLen);
          ??????}?else?{
          ????????(value?=?null),?(status?=?that.status.TIMEOUT);
          ????????that.remove(_key);
          ??????}
          ????}?else?{
          ??????status?=?that.status.FAILURE;
          ????}
          ????result?=?{
          ??????status:?status,
          ??????value:?value,
          ????};
          ????cb?&&?cb.call(this,?result.status,?result.value);
          ????return?result;
          ??},
          ??//?...
          };

          export?default?store;

          這樣, 我們就實(shí)現(xiàn)了每個(gè)?key?都有獨(dú)立的過(guò)期時(shí)間, 并且對(duì)不同的操作結(jié)果可以輕松的進(jìn)行狀態(tài)管控啦~

          4. 骨灰級(jí)解法

          當(dāng)然, 骨灰級(jí)解法是直接使用?xijs?這個(gè)?javascript?工具庫(kù), 因?yàn)槲乙呀?jīng)將上述完整實(shí)現(xiàn)方案封裝到該庫(kù)中了, 我們只需要使用如下的方案, 就能輕松使用具有過(guò)期時(shí)間的強(qiáng)大的?localStorage?方法啦 :

          //??先安裝?yarn?add?xijs
          import?{?store?}?from?'xijs';
          //?設(shè)置帶有過(guò)期時(shí)間的key
          store.set('name',?'dooring',?Date.now()?+?1000);
          console.log(store.get('name'));
          setTimeout(()?=>?{
          ??console.log(store.get('name'));
          },?1000);

          //?設(shè)置成功后的回調(diào)
          store.set('dooring',?'xuxiaoxi',?Date.now()?+?1000,?(status,?key,?value)?=>?{
          ??console.log('success');
          });

          同時(shí)?xijs?還在持續(xù)擴(kuò)充更有用的工具函數(shù), 讓業(yè)務(wù)開(kāi)發(fā)更高效. 目前已集成了如下工具函數(shù):

          • store?基于?localStorage?上層封裝的支持過(guò)期時(shí)間設(shè)置的緩存庫(kù), 支持操作回調(diào)
          • uuid?生成唯一id, 支持設(shè)置長(zhǎng)度
          • randomStr?生成指定個(gè)數(shù)的隨機(jī)字符串
          • formatDate?開(kāi)箱即用的時(shí)間格式化工具
          • debounce?防抖函數(shù)
          • throttle?節(jié)流函數(shù)
          • url2obj?將url字符串轉(zhuǎn)換為對(duì)象
          • obj2url?將對(duì)象轉(zhuǎn)換成編碼后的url字符串
          • isPC?判斷設(shè)備是否為PC類型

          github地址: https://github.com/MrXujiang/xijs?

          文檔地址: http://h5.dooring.cn/xijs/lib/store

          如果覺(jué)得有幫助, 不要忘記 star 哦~

          職業(yè)規(guī)劃??高級(jí)前端??可視化低代碼

          點(diǎn)擊上方?趣談前端,關(guān)注公眾號(hào)

          回復(fù)進(jìn)群,加入前端交流群



          點(diǎn)個(gè)在看你最好看

          瀏覽 29
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  日韩欧美18禁 | 操逼官网 | 国产婬片一级A片AAA毛片AⅤ | 人人操人人看人人摸 | 中国十大黄色操逼网站 |