狠狠色丁香婷婷综合尤物/久久精品综合一区二区三区/中国有色金属学报/国产日韩欧美在线观看 - 国产一区二区三区四区五区tv

LOGO OA教程 ERP教程 模切知識交流 PMS教程 CRM教程 開發文檔 其他文檔  
 
網站管理員

【WEB開發】瀏覽器 LocalStorage 還能這么用?

admin
2024年4月12日 23:17 本文熱度 752

本文你能學到

  1. 優雅的 Storage 工具類如何封裝(支持前綴key、加密存儲、過期時間,ts封裝等)
  2. localStorage 真實存儲大小/存儲統計
  3. localStorage 如何監聽
  4. localStorage 同源問題與同源窗口通信

前言

localStorage 使用是一個老生常談的話題,本文不講解基礎 api,主要教你如何封裝一個優雅的localStorage 工具,以及一些 localStorage中一些你不知道的知識點。

優雅的 Storage 工具如何封裝(前綴、加密、過期時間等)

該工具函數設計

  1. 采用工廠方法+閉包設計模式,不直接實例化類,而是根據傳入的參數來配置和返回一個 SmartStorage 的實例。
  2. 支持帶前綴的鍵:通過 prefixKey 參數可以為存儲的鍵名添加一個前綴,默認為空字符串。這個功能可以幫助避免鍵名沖突,特別是當在同一個域下的不同應用或組件中使用同一種存儲方式時。
  3. 支持過期時間:在存儲數據時,可以為每項數據設置一個過期時間(單位為秒),存儲的數據結構中會包括實際的值、存儲時間戳以及過期時間戳。在讀取數據時,會檢查數據是否過期,如果已經過期,則自動刪除
  4. 支持加密存儲:存儲數據時根據參數配置可先進行加密,讀取數據時再解密,加密使用的 crypto 模塊
  5. 錯誤處理:在讀取數據時,如果解密過程出錯或數據格式不正確,會捕獲異常并返回默認值,這提高了程序的健壯性。
  6. 支持常用的 apiset get remove clear
  7. TypeScript 實現

接下來是代碼實現:在未進行代碼實現前可以基于上面的設計自己實現一下,然后對照下我的代碼實現

/**
 * 封裝一個local
 */

import { decrypt as aesDecrypt, encrypt as aesEncrypt } from 'crypto-js/aes';
import UTF8, { parse } from 'crypto-js/enc-utf8';
import pkcs7 from 'crypto-js/pad-pkcs7';
import CTR from 'crypto-js/mode-ctr';
import {isNil} from 'lodash';


interface EncryptionParams {
    key: string;
    iv: string;
}

export interface Encryption {
    encrypt(plainText: string): string;
    decrypt(cipherText: string): string;
}

/**
 * 加密類簡單實現
 */

class AesEncryption implements Encryption {
    private readonly key;
    private readonly iv;

    constructor({ key, iv }: EncryptionParams) {
        this.key = parse(key);
        this.iv = parse(iv);
    }

    get getOptions() {
        return {
            mode: CTR, // 加密部分不贅余,自行搜索參數學習
            padding: pkcs7, // 加密部分不贅余,自行搜索參數學習
            iv: this.iv,
        };
    }

    encrypt(plainText: string) {
        return aesEncrypt(plainText, this.key, this.getOptions).toString();
    }

    decrypt(cipherText: string) {
        return aesDecrypt(cipherText, this.key, this.getOptions).toString(UTF8);
    }
}


export interface CreateSmartStorageParams extends EncryptionParams {
    prefixKey: string;
    storage: Storage;
    hasEncrypt: boolean;
    timeout?: number;
}
/**
 * localStorage工廠方法實現
 * @param param0 
 * @returns 
 */

export const createSmartStorage = ({
    prefixKey = '',
    storage = localStorage, // 這里其實也可以支持sessionStorage,自行配置
    key = cacheConfig.key, // 修改為自己項目cacheConfig中的key
    iv = cacheConfig.iv, // 修改為自己項目cacheConfig中的iv
    timeout = null,
    hasEncrypt = true,
}: Partial<CreateSmartStorageParams> = {}) => {
    if (hasEncrypt && [key.length, iv.length].some((item) => item !== 16)) {
        throw new Error('When hasEncrypt is true, the key or iv must be 16 bits!');
    }
    // 
    const persistEncryption: Encryption = new AesEncryption({
        key: cacheConfig.key,// 修改為自己項目cacheConfig中的key
        iv: cacheConfig.iv,// 修改為自己項目cacheConfig中的iv
    })
    /**
     * Cache class
     * Construction parameters can be passed intolocalStorage,
     * @class Cache
     * @example
     */

    const SmartStorage = class SmartStorage {
        private storage: Storage;
        private prefixKey?: string;
        private encryption: Encryption;
        private hasEncrypt: boolean;
        /**
         *
         * @param {*} storage
         */

        constructor() {
            this.storage = storage;
            this.prefixKey = prefixKey;
            this.encryption = persistEncryption;
            this.hasEncrypt = hasEncrypt;
        }

        private getKey(key: string) {
            return `${this.prefixKey}${key}`.toUpperCase();
        }

        /**
         * Set cache
         * @param {string} key
         * @param {*} value
         * @param {*} expire Expiration time in seconds
         * @memberof Cache
         */

        set(key: string, value: any, expire: number | null = timeout) {
            const stringData = JSON.stringify({
                value,
                time: Date.now(),
                expire: !isNil(expire) ? new Date().getTime() + expire * 1000 : null,
            });
            const stringifyValue = this.hasEncrypt ? this.encryption.encrypt(stringData) : stringData;
            this.storage.setItem(this.getKey(key), stringifyValue);
        }

        /**
         * Read cache
         * @param {string} key
         * @param {*} def
         * @memberof Cache
         */

        get(key: string, def: any = null): any {
            const val = this.storage.getItem(this.getKey(key));
            if (!val) return def;

            try {
                const decVal = this.hasEncrypt ? this.encryption.decrypt(val) : val;
                const data = JSON.parse(decVal);
                const { value, expire } = data;
                if (isNil(expire) || expire >= new Date().getTime()) {
                    return value;
                }
                this.remove(key);
            } catch (e) {
                return def;
            }
        }

        /**
         * Delete cache based on key
         * @param {string} key
         * @memberof Cache
         */

        remove(key: string) {
            this.storage.removeItem(this.getKey(key));
        }

        /**
         * Delete all caches of this instance
         */

        clear(): void {
            this.storage.clear();
        }
    };
    return new SmartStorage();
}; 

再補充幾個 localStorage 相關可能你不知道的知識點。

localStorage 存儲大小

  1. localStorage 的存儲空間是 5M,但是單位是字符串的長度值, 或者 utf-16 的編碼單元,也可以說是 10M 字節空間。
  2. localStorage 的 key 鍵也是占存儲空間的。
  3. localStorage 如何統計已使用空間
function sieOfLS({
    return Object.entries(localStorage).map(v => v.join('')).join('').length;
}

這個函數也可以加到storage工具函數中

localStorage.clear();
localStorage.setItem("🌞"1);
localStorage.setItem("🌞🌞🌞🌞"1111);
console.log("size:", sieOfLS())   // 15
// 🌞*5 + 1 *5 = 2*5 + 1*5 = 15

localStorage 如何監聽

  1. 原生 api 監聽
window.addEventListener('storage', () => {
  // callback
})

每次 localStorage 中有任何變動都會觸發一個 storage 事件,即使是同域下的不同頁面A、B都會監聽這個事件,一旦有窗口更新 localStorage,其他窗口都會收到通知。

  1. 基于我們前面封裝的 localStorage 工具類 在封裝后每一個函數內部可以進行監聽,同時如果想要統計監聽一些內容,可以給一些函數增加 aop 裝飾器來完成。
@aop
set(key: string, value: any, expire: number | null = timeout) {
            const stringData = JSON.stringify({
                value,
                time: Date.now(),
                expire: !isNil(expire) ? new Date().getTime() + expire * 1000 : null,
            });
            const stringifyValue = this.hasEncrypt ? this.encryption.encrypt(stringData) : stringData;
            this.storage.setItem(this.getKey(key), stringifyValue);
        }

具體 aop 裝飾器相關內容可以看我另一篇文章,本文只講解 localStorage

localStorage 同源

只有來自同一個源的網頁才能訪問相同的 localStorage 對應 key 的數據,這也是前面工具類封裝,這個參數 prefixKey 的作用,同源項目可以加一個唯一 key,保證同源下的 localStorage 不沖突。

這是一個需要避免的問題,有時候也會基于這些實現一些功能,比如下面的同源窗口通信

同源窗口通信

我們就可以只有一個窗口與后臺建立連接,收到更新后,廣播給其他窗口就可以。想象這樣一個場景:當localStorage 中的數據發生變化時,瀏覽器會觸發一個 storage 事件,這個事件能夠被同一源下所有的窗口監聽到。這意味著,如果一個窗口更新了 localStorage ,其他窗口可以實時接收到這一變動的通知。雖然這個機制的原理相對簡單——基于事件的廣播,但是要搭建一個功能完備的跨窗口通信機制,則需要考慮更多的細節和挑戰。

  1. 每個窗口都需要有一個獨一無二的標識符(ID),以便在眾多窗口中準確識別和管理它們。
  2. 為了避免同一個消息被重復處理,必須有機制確保消息的唯一性。
  3. 還需要確保只有那些真正需要接收特定消息的窗口才會收到通知,這就要求消息分發機制能夠有效地過濾掉不相關的窗口。
  4. 考慮到窗口可能會因為各種原因關閉或變得不響應,引入窗口的“心跳”機制來監控它們的活躍狀態變得尤為重要。
  5. 當涉及到需要從多個窗口中選舉出一個主窗口來協調操作時,主窗口的選舉機制也是不可或缺的一環。

盡管這些要求聽起來可能令人望而卻步,不過開源社區已經提供了一些優秀的解決方案來簡化這個過程。例如,diy/intercom.js和tejacques/crosstab 這兩個庫就是專門為了解決跨窗口通信而設計的。感興趣的學習下

擴展知識 同源策略

  • 協議相同:網頁地址的協議必須相同。例如,http://example.com 和 https://example.com 被視為不同的源,因此如果一個頁面是通過 HTTP 加載的,它不能訪問通過HTTPS加載的頁面的 LocalStorage 數據,反之亦然。

  • 域名相同:網頁的域名必須完全相同。子域與主域也被視為不同的源(例如,sub.example.com與 example.com ),默認情況下,它們無法共享 LocalStorage 數據。


  • 端口相同:即使協議和域名相同,端口號的不同也會使它們成為不同的源。例如,http://example.com:80 和 http://example.com:8080 是不同的源。

總結

相信學習完本文你對 LocalStorage有一個徹底的掌握,創作不易歡迎三連支持。


該文章在 2024/4/12 23:17:25 編輯過
關鍵字查詢
相關文章
正在查詢...
點晴ERP是一款針對中小制造業的專業生產管理軟件系統,系統成熟度和易用性得到了國內大量中小企業的青睞。
點晴PMS碼頭管理系統主要針對港口碼頭集裝箱與散貨日常運作、調度、堆場、車隊、財務費用、相關報表等業務管理,結合碼頭的業務特點,圍繞調度、堆場作業而開發的。集技術的先進性、管理的有效性于一體,是物流碼頭及其他港口類企業的高效ERP管理信息系統。
點晴WMS倉儲管理系統提供了貨物產品管理,銷售管理,采購管理,倉儲管理,倉庫管理,保質期管理,貨位管理,庫位管理,生產管理,WMS管理系統,標簽打印,條形碼,二維碼管理,批號管理軟件。
點晴免費OA是一款軟件和通用服務都免費,不限功能、不限時間、不限用戶的免費OA協同辦公管理系統。
Copyright 2010-2025 ClickSun All Rights Reserved