欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

使用localForage實(shí)現(xiàn)帶過期時(shí)間的本地存儲(chǔ)方案

 更新時(shí)間:2024年09月11日 08:46:45   作者:pe7er  
在前端開發(fā)中,我們經(jīng)常需要將數(shù)據(jù)存儲(chǔ)在客戶端,以減少網(wǎng)絡(luò)請(qǐng)求次數(shù),提高用戶體驗(yàn),localStorage 和 sessionStorage 是常用的存儲(chǔ)方案,但它們有一些局限性,為了解決這些問題,本文將介紹如何使用 localForage 實(shí)現(xiàn)一個(gè)帶過期時(shí)間的本地存儲(chǔ)方案,需要的朋友可以參考下

前言

在前端開發(fā)中,我們經(jīng)常需要將數(shù)據(jù)存儲(chǔ)在客戶端,以減少網(wǎng)絡(luò)請(qǐng)求次數(shù),提高用戶體驗(yàn)。localStoragesessionStorage 是常用的存儲(chǔ)方案,但它們有一些局限性,例如同步 API、只能存儲(chǔ)字符串以及大小限制等。為了解決這些問題,本文將介紹如何使用 localForage 實(shí)現(xiàn)一個(gè)帶過期時(shí)間的本地存儲(chǔ)方案。

什么是 localForage?

localForage 是一個(gè)優(yōu)雅的本地存儲(chǔ)庫,提供了異步的 API,支持存儲(chǔ)多種類型的數(shù)據(jù)(如對(duì)象、數(shù)組、二進(jìn)制數(shù)據(jù)等),并且在內(nèi)部優(yōu)先使用 IndexedDB,如果不可用則回退到 WebSQL 或 localStorage。

需求分析

我們希望實(shí)現(xiàn)以下功能:

  • 數(shù)據(jù)存儲(chǔ):能夠存儲(chǔ)任意類型的數(shù)據(jù)。
  • 過期時(shí)間:支持設(shè)置數(shù)據(jù)的過期時(shí)間,過期后自動(dòng)清除。
  • 持久化:數(shù)據(jù)在刷新或重新打開頁面后仍然存在,直到過期時(shí)間到達(dá)。

實(shí)現(xiàn)思路

為了實(shí)現(xiàn)帶過期時(shí)間的本地存儲(chǔ),我們需要在存儲(chǔ)數(shù)據(jù)的同時(shí),記錄其過期時(shí)間。在讀取數(shù)據(jù)時(shí),先檢查是否過期,若未過期則返回?cái)?shù)據(jù),否則刪除數(shù)據(jù)并返回 null。

代碼實(shí)現(xiàn)

下面是具體的代碼實(shí)現(xiàn):

import localforage from 'localforage';

// 配置 localForage
localforage.config({
  name: '存儲(chǔ)名稱,請(qǐng)根據(jù)項(xiàng)目名稱和需求來命名',
});

// 定義一個(gè)永不過期的標(biāo)志
const NEVER_EXPIRES_FLAG = -1;

/**
 * 設(shè)置存儲(chǔ)項(xiàng)
 * @param k 鍵名
 * @param v 值
 * @param expired 過期時(shí)間(分鐘),默認(rèn)永不過期
 * @returns Promise
 */
export const setItem = (k: string, v: any, expired: number = -1) => {
  const expiredKey = `${k}__expires__`;
  let exp = 0;

  if (expired === NEVER_EXPIRES_FLAG) {
    exp = NEVER_EXPIRES_FLAG;
  } else if (expired >= 0) {
    exp = Date.now() + 1000 * 60 * expired;
  }

  // 存儲(chǔ)過期時(shí)間
  localforage.setItem(expiredKey, exp.toString()).catch((err) => {
    console.error('設(shè)置過期時(shí)間失敗:', err);
  });

  // 存儲(chǔ)實(shí)際數(shù)據(jù)
  return localforage.setItem(k, v);
};

/**
 * 獲取存儲(chǔ)項(xiàng)
 * @param k 鍵名
 * @returns Promise<any | null>
 */
export const getItem = async (k: string) => {
  const expiredKey = `${k}__expires__`;

  try {
    const expiredValue = await localforage.getItem<string | null>(expiredKey);

    if (expiredValue === null) {
      // 未設(shè)置過期時(shí)間,視為不存在
      return null;
    }

    const expiredTime = parseInt(expiredValue, 10);

    if (expiredTime === NEVER_EXPIRES_FLAG) {
      // 永不過期
      return localforage.getItem(k);
    }

    if (expiredTime > Date.now()) {
      // 未過期,返回?cái)?shù)據(jù)
      return localforage.getItem(k);
    } else {
      // 已過期,刪除數(shù)據(jù)
      removeItem(k);
      removeItem(expiredKey);
      return null;
    }
  } catch (err) {
    console.error('獲取數(shù)據(jù)失敗:', err);
    return null;
  }
};

/**
 * 刪除存儲(chǔ)項(xiàng)
 * @param k 鍵名
 * @returns Promise
 */
export const removeItem = (k: string) => {
  const expiredKey = `${k}__expires__`;
  localforage.removeItem(expiredKey).catch((err) => {
    console.error('刪除過期時(shí)間失敗:', err);
  });
  return localforage.removeItem(k);
};

代碼解析

配置 localForage

localforage.config({
  name: 'bdsg-client',
});
  • name:為應(yīng)用程序指定一個(gè)名稱,便于在瀏覽器中區(qū)分存儲(chǔ)。

定義永不過期的標(biāo)志

const NEVER_EXPIRES_FLAG = -1;
  • 使用 -1 作為永不過期的標(biāo)志。

設(shè)置存儲(chǔ)項(xiàng)

export const setItem = (k: string, v: any, expired: number = -1) => {
  const expiredKey = `${k}__expires__`;
  let exp = 0;

  if (expired === NEVER_EXPIRES_FLAG) {
    exp = NEVER_EXPIRES_FLAG;
  } else if (expired >= 0) {
    exp = Date.now() + 1000 * 60 * expired;
  }

  // 存儲(chǔ)過期時(shí)間
  localforage.setItem(expiredKey, exp.toString()).catch((err) => {
    console.error('設(shè)置過期時(shí)間失敗:', err);
  });

  // 存儲(chǔ)實(shí)際數(shù)據(jù)
  return localforage.setItem(k, v);
};
  • 參數(shù)說明
    • k:鍵名。
    • v:值。
    • expired:過期時(shí)間,單位為分鐘,默認(rèn)為 -1(永不過期)。
  • 實(shí)現(xiàn)邏輯
    • 生成一個(gè)對(duì)應(yīng)的過期時(shí)間鍵名 expiredKey。
    • 根據(jù)過期時(shí)間計(jì)算實(shí)際的過期時(shí)間戳 exp
      • 如果 expired-1,則設(shè)為 NEVER_EXPIRES_FLAG。
      • 如果 expired 大于等于 0,則計(jì)算未來的時(shí)間戳。
    • 使用 localforage.setItem 分別存儲(chǔ)過期時(shí)間和實(shí)際數(shù)據(jù)。

獲取存儲(chǔ)項(xiàng)

export const getItem = async (k: string) => {
  const expiredKey = `${k}__expires__`;

  try {
    const expiredValue = await localforage.getItem<string | null>(expiredKey);

    if (expiredValue === null) {
      // 未設(shè)置過期時(shí)間,視為不存在
      return null;
    }

    const expiredTime = parseInt(expiredValue, 10);

    if (expiredTime === NEVER_EXPIRES_FLAG) {
      // 永不過期
      return localforage.getItem(k);
    }

    if (expiredTime > Date.now()) {
      // 未過期,返回?cái)?shù)據(jù)
      return localforage.getItem(k);
    } else {
      // 已過期,刪除數(shù)據(jù)
      removeItem(k);
      removeItem(expiredKey);
      return null;
    }
  } catch (err) {
    console.error('獲取數(shù)據(jù)失敗:', err);
    return null;
  }
};
  • 實(shí)現(xiàn)邏輯
    • 先獲取對(duì)應(yīng)的過期時(shí)間 expiredValue
      • 如果未設(shè)置過期時(shí)間,直接返回 null
    • 將過期時(shí)間字符串轉(zhuǎn)換為數(shù)字 expiredTime。
    • 根據(jù)過期時(shí)間判斷:
      • 如果是永不過期標(biāo)志,直接返回?cái)?shù)據(jù)。
      • 如果未過期(expiredTime > Date.now()),返回?cái)?shù)據(jù)。
      • 如果已過期,刪除數(shù)據(jù)并返回 null

刪除存儲(chǔ)項(xiàng)

export const removeItem = (k: string) => {
  const expiredKey = `${k}__expires__`;
  localforage.removeItem(expiredKey).catch((err) => {
    console.error('刪除過期時(shí)間失敗:', err);
  });
  return localforage.removeItem(k);
};
  • 實(shí)現(xiàn)邏輯
    • 同時(shí)刪除數(shù)據(jù)和對(duì)應(yīng)的過期時(shí)間。

使用示例

// 存儲(chǔ)數(shù)據(jù),設(shè)置過期時(shí)間為 5 分鐘
setItem('userInfo', { name: '張三', age: 28 }, 5);

// 獲取數(shù)據(jù)
getItem('userInfo').then((data) => {
  if (data) {
    console.log('用戶信息:', data);
  } else {
    console.log('用戶信息已過期或不存在');
  }
});

// 刪除數(shù)據(jù)
removeItem('userInfo');

注意事項(xiàng)

  • 異步操作:localForage 的所有方法都是異步的,返回的是 Promise,所以在獲取數(shù)據(jù)時(shí)需要使用 thenasync/await
  • 數(shù)據(jù)類型:localForage 支持存儲(chǔ)多種數(shù)據(jù)類型,包括對(duì)象、數(shù)組、Blob 等。
  • 錯(cuò)誤處理:在實(shí)際開發(fā)中,應(yīng)對(duì)可能出現(xiàn)的錯(cuò)誤進(jìn)行處理,以提高代碼的健壯性。

多實(shí)例存儲(chǔ)

上面的代碼實(shí)例全局只使用了一個(gè)實(shí)例存儲(chǔ),如果希望使用多實(shí)例存儲(chǔ),可以進(jìn)行簡單的修改,下面是一個(gè)使用組合函數(shù)的方式實(shí)現(xiàn)多實(shí)例存儲(chǔ)的代碼。

import localforage from 'localforage';

export const useLocalforage = (options: LocalForageOptions ) => {
  // 配置 localForage
  const store = localforage.createInstance({
    ...options,
  });

  // 定義一個(gè)永不過期的標(biāo)志
  const NEVER_EXPIRES_FLAG = -1;

  /**
   * 設(shè)置存儲(chǔ)項(xiàng)
   * @param k 鍵名
   * @param v 值
   * @param expired 過期時(shí)間(分鐘),默認(rèn)永不過期
   * @returns Promise
   */
  const setItem = (k: string, v: any, expired: number = -1): Promise<void> => {
    const expiredKey = `${k}__expires__`;
    let exp = 0;

    if (expired === NEVER_EXPIRES_FLAG) {
      exp = NEVER_EXPIRES_FLAG;
    } else if (expired >= 0) {
      exp = Date.now() + 1000 * 60 * expired;
    }

    // 存儲(chǔ)過期時(shí)間
    store.setItem(expiredKey, exp.toString()).catch((err) => {
      console.error('設(shè)置過期時(shí)間失敗:', err);
    });

    // 存儲(chǔ)實(shí)際數(shù)據(jù)
    return store.setItem(k, v);
  };

  /**
   * 獲取存儲(chǔ)項(xiàng)
   * @param k 鍵名
   * @returns Promise<T | null>
   */
  const getItem = async <T> (k: string) : Promise<T | null> => {
    const expiredKey = `${k}__expires__`;

    try {
      const expiredValue = await store.getItem<string | null>(expiredKey);

      if (expiredValue === null) {
        // 未設(shè)置過期時(shí)間,視為不存在
        return null;
      }

      const expiredTime = parseInt(expiredValue, 10);

      if (expiredTime === NEVER_EXPIRES_FLAG) {
        // 永不過期
        return store.getItem(k) as T;
      }

      if (expiredTime > Date.now()) {
        // 未過期,返回?cái)?shù)據(jù)
        return store.getItem(k);
      } else {
        // 已過期,刪除數(shù)據(jù)
        removeItem(k);
        removeItem(expiredKey);
        return null;
      }
    } catch (err) {
      console.error('獲取數(shù)據(jù)失敗:', err);
      return null;
    }
  };

  /**
   * 刪除存儲(chǔ)項(xiàng)
   * @param k 鍵名
   * @returns Promise
   */
  const removeItem = (k: string) => {
    const expiredKey = `${k}__expires__`;
    store.removeItem(expiredKey).catch((err) => {
      console.error('刪除過期時(shí)間失敗:', err);
    });
    return store.removeItem(k);
  };

  return {
    getItem,
    setItem,
  }
}

export default useLocalforage;

使用示例

<script setup lang="ts">
import { onMounted, ref } from "vue";
import useLocalForage from "./use-local-forage";
import { USER_VISITOR_COUNT, SHOW_NAVIGATOR_BOOL } from "./storage-constants";
import Demo from './Demo.vue';

const localForageInstance = useLocalForage({
  name: "test",
  storeName: 'storeName'
});

const visitorCount = ref(0);

const loadStorage = async () => {
  try {
    const data = await localForageInstance.getItem<number>(USER_VISITOR_COUNT);
    visitorCount.value = data || 0;
  } catch (error) {
    console.error(error);
  } finally {
    recordVisitorCount();
  }
};
const recordVisitorCount = () => {
  localForageInstance.setItem(USER_VISITOR_COUNT, visitorCount.value + 1);
};

onMounted(() => {
  loadStorage();
})
</script>

<template>
  <h1 v-show="visitorCount">用戶訪問次數(shù){{ visitorCount }}次</h1>
  <Demo  />
</template>

不同之處是使用const store = localforage.createInstance來創(chuàng)建實(shí)例,每次使用創(chuàng)建的store 來進(jìn)行操作,并且會(huì)根據(jù)命名來存放數(shù)據(jù),這對(duì)于分類管理數(shù)據(jù)非常有用。

當(dāng)然如果命名相同,就會(huì)存放在一個(gè)庫中,但建議根據(jù)功能來區(qū)分?jǐn)?shù)據(jù)。比如項(xiàng)目數(shù)據(jù)存放在一個(gè)庫中,數(shù)據(jù) mock 存放在另一個(gè)庫中。

總結(jié)

通過以上實(shí)現(xiàn),我們可以方便地使用 localForage 來存儲(chǔ)帶過期時(shí)間的數(shù)據(jù)。相比傳統(tǒng)的 localStorage,localForage 提供了更強(qiáng)大的功能和更好的性能,適用于大多數(shù)前端存儲(chǔ)場景。

本案例的所有代碼托管在:multi-localforage-demo

以上就是使用localForage實(shí)現(xiàn)帶過期時(shí)間的本地存儲(chǔ)方案的詳細(xì)內(nèi)容,更多關(guān)于localForage本地存儲(chǔ)方案的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評(píng)論