亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

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

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

前言

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

什么是 localForage?

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

需求分析

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

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

實現(xiàn)思路

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

代碼實現(xiàn)

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

import localforage from 'localforage';

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

// 定義一個永不過期的標志
const NEVER_EXPIRES_FLAG = -1;

/**
 * 設(shè)置存儲項
 * @param k 鍵名
 * @param v 值
 * @param expired 過期時間(分鐘),默認永不過期
 * @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;
  }

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

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

/**
 * 獲取存儲項
 * @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è)置過期時間,視為不存在
      return null;
    }

    const expiredTime = parseInt(expiredValue, 10);

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

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

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

代碼解析

配置 localForage

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

定義永不過期的標志

const NEVER_EXPIRES_FLAG = -1;
  • 使用 -1 作為永不過期的標志。

設(shè)置存儲項

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;
  }

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

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

獲取存儲項

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

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

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

    const expiredTime = parseInt(expiredValue, 10);

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

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

刪除存儲項

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

使用示例

// 存儲數(shù)據(jù),設(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');

注意事項

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

多實例存儲

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

import localforage from 'localforage';

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

  // 定義一個永不過期的標志
  const NEVER_EXPIRES_FLAG = -1;

  /**
   * 設(shè)置存儲項
   * @param k 鍵名
   * @param v 值
   * @param expired 過期時間(分鐘),默認永不過期
   * @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;
    }

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

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

  /**
   * 獲取存儲項
   * @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è)置過期時間,視為不存在
        return null;
      }

      const expiredTime = parseInt(expiredValue, 10);

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

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

  /**
   * 刪除存儲項
   * @param k 鍵名
   * @returns Promise
   */
  const removeItem = (k: string) => {
    const expiredKey = `${k}__expires__`;
    store.removeItem(expiredKey).catch((err) => {
      console.error('刪除過期時間失敗:', 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)建實例,每次使用創(chuàng)建的store 來進行操作,并且會根據(jù)命名來存放數(shù)據(jù),這對于分類管理數(shù)據(jù)非常有用。

當然如果命名相同,就會存放在一個庫中,但建議根據(jù)功能來區(qū)分數(shù)據(jù)。比如項目數(shù)據(jù)存放在一個庫中,數(shù)據(jù) mock 存放在另一個庫中。

總結(jié)

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

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

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

相關(guān)文章

最新評論