欧美成人精品手机在线观看_69视频国产_动漫精品第一页_日韩中文字幕网 - 日本欧美一区二区

LOGO OA教程 ERP教程 模切知識(shí)交流 PMS教程 CRM教程 開(kāi)發(fā)文檔 其他文檔  
 
網(wǎng)站管理員

如何優(yōu)雅地重寫 localStorage 、sessionStorage 方法?已封裝,項(xiàng)目可直接使用!

admin
2024年12月11日 11:25 本文熱度 372
?

需求簡(jiǎn)介

在前端開(kāi)發(fā)中,localStorage 和 sessionStorage 是非常常見(jiàn)的數(shù)據(jù)存儲(chǔ)解決方案。但在某些特殊場(chǎng)景下,原生的 localStorage 和 sessionStorage 無(wú)法滿足業(yè)務(wù)需求,例如:

  • 「業(yè)務(wù)定制化需求」:需要在存儲(chǔ)和獲取某些特定鍵時(shí)加入邏輯,比如數(shù)據(jù)加密、校驗(yàn)或默認(rèn)值填充。
  • 「全局監(jiān)控」:希望對(duì)存儲(chǔ)和讀取操作進(jìn)行監(jiān)控,例如記錄關(guān)鍵數(shù)據(jù)的訪問(wèn)日志或統(tǒng)計(jì)操作頻率。
  • 「系統(tǒng)數(shù)據(jù)保護(hù)」:防止外部代碼對(duì)特定鍵值的誤改動(dòng)。

在上面的場(chǎng)景中,我們通過(guò)重寫原生的 localStorage 和 sessionStorage 的方法,就可以實(shí)現(xiàn)這些特殊的需求。

技術(shù)方案

核心思路

要重寫window上原生的方法,我們要先將原生的 setItem 和 getItem 方法保留下來(lái),以便在需要時(shí)調(diào)用。然后,通過(guò)下面的偽代碼重寫方法,在存儲(chǔ)或讀取過(guò)程中加入自定義邏輯。

const _setItem = localStorage.setItem;
localStorage.setItem = function (...args{
    // 自定義邏輯....
    // 最終調(diào)用_setItem
};

最后,我們也可以提供恢復(fù)原方法的機(jī)制,確保代碼可控,不影響其他功能。

由于我們的重寫的是window上的方法,因此,重寫的時(shí)機(jī)一定「要盡可能的早」。比如,我們使用的是vue項(xiàng)目,我們就應(yīng)該在vue實(shí)例創(chuàng)建前,實(shí)現(xiàn)原生方法的重寫:

import { createApp } from 'vue';
import App from './App.vue';
import router from './router';
import store from './store';

// 代理 localStorage 和 sessionStorage 方法
function proxyStorage(storage{
  // ...
}

// 代理 localStorage 和 sessionStorage
proxyStorage(localStorage);
proxyStorage(sessionStorage);

// 創(chuàng)建 Vue 應(yīng)用
const app = createApp(App);

// 使用路由和狀態(tài)管理
app.use(router).use(store);

// 掛載應(yīng)用

代理存儲(chǔ)方法

初步實(shí)現(xiàn):簡(jiǎn)單攔截

我們可以實(shí)現(xiàn)一個(gè)簡(jiǎn)單的代理,針對(duì)特定鍵值在存儲(chǔ)和讀取時(shí)加入邏輯(根據(jù)業(yè)務(wù)而定)。例如:

function proxyStorage(storage{
  // 保存原始方法
  const originalSetItem = storage.setItem;
  const originalGetItem = storage.getItem;

  // 重寫方法
  storage.setItem = function (key, value{
    // 自定義邏輯,比如拒絕用戶修改system屬性
    if (key === 'system') {
        retrun
    }
    originalSetItem.call(this, key, value);
  };

  storage.getItem = function (key{
    // 自定義邏輯,比如用戶讀取system屬性,始終返回固定值
    if (key === 'system') {
      return "對(duì)不起,你無(wú)權(quán)讀取用戶信息"
    }
    return originalGetItem.call(this, key);
  };
}

// 代理 localStorage 和 sessionStorage
proxyStorage(localStorage);
proxyStorage(sessionStorage);

上述代碼很簡(jiǎn)單,你可能有疑問(wèn)的就是為什么調(diào)用原生的方法時(shí),我們要使用call?

originalSetItem.call(this, key, value);

這是因?yàn)?code style="-webkit-tap-highlight-color: transparent; margin: 0px 2px; padding: 2px 4px; outline: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important; border-radius: 4px; background-color: rgba(27, 31, 35, 0.05); font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace; word-break: break-all;">originalGetItem 和 originalSetItem 是從 localStorage 或 sessionStorage 的原型方法保存下來(lái)的引用。如果直接調(diào)用 originalSetItem(key, value) 或 originalGetItem('origin_system'),它們的上下文(this)會(huì)丟失。

const setItem = localStorage.setItem;
setItem('key''value'); // 會(huì)報(bào)錯(cuò):Cannot read properties of undefined

這是因?yàn)?nbsp;setItem 的上下文丟失,它不再知道自己屬于 localStorage

提供靈活的配置能力

為了應(yīng)對(duì)更多場(chǎng)景需求,我們可以引入配置選項(xiàng),讓代理邏輯更加靈活,比如,加入自定義鉤子函數(shù),允許用戶自定義重寫的邏輯。

function proxyStorage(storage, config = {}{
  const originalSetItem = storage.setItem;
  const originalGetItem = storage.getItem;

  // 提供給用戶的鉤子函數(shù)
  const beforeSetItem = config.beforeSetItem || ((key, value) => [key, value]);
  const afterGetItem = config.afterGetItem || ((key, value) => value);

  storage.setItem = function (key, value{
    // 調(diào)用用戶定義的 beforeSetItem 鉤子
    const [newKey, newValue] = beforeSetItem(key, value) || [key, value];
    if (newKey !== undefined && newValue !== undefined) {
      originalSetItem.call(this, newKey, newValue);
    }esle{
      originalSetItem.call(this, key, value);
    }
  };

  storage.getItem = function (key{
    const originalValue = originalGetItem.call(this, key);
    // 調(diào)用用戶定義的 afterGetItem 鉤子
    return afterGetItem(key, originalValue);
  };

}

上述代碼中,beforeSetItem、afterGetItem是我們自定義鉤子函數(shù),可以實(shí)現(xiàn)自定義返回值、讀取值的邏輯。我們看看它有什么實(shí)際使用場(chǎng)景:

「示例 1:加密存儲(chǔ)數(shù)據(jù)」

import CryptoJS from 'crypto-js';

const secretKey = '私有加密秘鑰';

proxyStorage(localStorage, {
  beforeSetItem(key, value) => {
    const encryptedValue = CryptoJS.AES.encrypt(value, secretKey).toString();
    return [key, encryptedValue];
  },
  afterGetItem(key, value) => {
    try {
      const bytes = CryptoJS.AES.decrypt(value, secretKey);
      return bytes.toString(CryptoJS.enc.Utf8) || null;
    } catch (error) {
      return null;
    }
  },
});

// 使用代理后的 localStorage
localStorage.setItem('sensitiveData''my-secret-data'); // 數(shù)據(jù)將被加密存儲(chǔ)
console.log(localStorage.getItem('sensitiveData')); // 數(shù)據(jù)將被解密返回

上述代碼實(shí)現(xiàn)了在存儲(chǔ)數(shù)據(jù)時(shí)加密,在讀取數(shù)據(jù)時(shí)解密的功能,非常具有實(shí)用價(jià)值!

「示例 2:監(jiān)控存儲(chǔ)操作」

「記錄存儲(chǔ)和讀取行為:」

proxyStorage(localStorage, {
  beforeSetItem(key, value) => {
    console.log(`設(shè)置值: key=${key}, value=${value}`);
    // 設(shè)置值的其他記錄邏輯
    return [key, value]; // 不修改原始行為
  },
  afterGetItem(key, value) => {
    console.log(`讀取值: key=${key}, value=${value}`);
    //讀取值的其他記錄邏輯
    return value; // 不修改原始行為
  },
});

// 使用代理后的 localStorage
localStorage.setItem('exampleKey''exampleValue');
console.log(localStorage.getItem('exampleKey'));

「示例 3:攔截特定鍵值」

阻止某些特定鍵的存儲(chǔ)或讀取:

proxyStorage(localStorage, {
  beforeSetItem(key, value) => {
    if (key === 'admin') {
      console.warn(`您無(wú)權(quán)操作`);
      return// 攔截存儲(chǔ)操作
    }
    return [key, value];
  },
  afterGetItem(key, value) => {
    if (key === 'admin') {
      console.warn(`您無(wú)權(quán)操作`);
      return 'error'// 返回自定義值
    }
    return value;
  },
});

// 使用代理后的 localStorage
localStorage.setItem('admin''secretValue'); // 被攔截
console.log(localStorage.getItem('admin')); // 輸出: error

取消代理

在某些場(chǎng)景,我們可能需要取消代理,比如,當(dāng)我們從A頁(yè)面切換到B頁(yè)面時(shí),我們可能需要終止代理。因此,我們需要提供一個(gè)終止代理的方法。

function proxyStorage(storage, config = {}{
  const originalSetItem = storage.setItem;
  const originalGetItem = storage.getItem;

  // 提供給用戶的鉤子函數(shù)
  const beforeSetItem = config.beforeSetItem || ((key, value) => [key, value]);
  const afterGetItem = config.afterGetItem || ((key, value) => value);

  storage.setItem = function (key, value{
    // 調(diào)用用戶定義的 beforeSetItem 鉤子
    const [newKey, newValue] = beforeSetItem(key, value) || [key, value];
    if (newKey !== undefined && newValue !== undefined) {
      originalSetItem.call(this, newKey, newValue);
    }esle{
      originalSetItem.call(this, key, value);
    }
  };

  storage.getItem = function (key{
    const originalValue = originalGetItem.call(this, key);
    // 調(diào)用用戶定義的 afterGetItem 鉤子
    return afterGetItem(key, originalValue);
  };

  const unproxy = () => {
    storage.setItem = originalSetItem;
    storage.getItem = originalGetItem;
  };

  return unproxy;
  
}

使用示例

// 代理 localStorage
const unproxy = proxyStorage(localStorage, config);

// 使用 localStorage
localStorage.setItem('key''12345'); // 被攔截

// 恢復(fù)原始方法
unproxyLocalStorage();

整合后的最終代碼

我們可以將這個(gè)方法直接封裝成一個(gè)類,方便調(diào)用

class StorageProxy {
  constructor(storage, config = {}) {
    if (StorageProxy.instance) {
      return StorageProxy.instance; // 返回已存在的實(shí)例
    }

    this.storage = storage;
    this.config = config;

    // 保存原始方法
    this.originalSetItem = storage.setItem;
    this.originalGetItem = storage.getItem;

    // 提供默認(rèn)的鉤子函數(shù)
    this.beforeSetItem = config.beforeSetItem || ((key, value) => [key, value]);
    this.afterGetItem = config.afterGetItem || ((key, value) => value);

    // 初始化代理方法
    this.proxyMethods();

    // 緩存當(dāng)前實(shí)例
    StorageProxy.instance = this;
  }

  proxyMethods() {
    const { storage, beforeSetItem, afterGetItem, originalSetItem, originalGetItem } = this;

    storage.setItem = function (key, value{
      const [newKey, newValue] = beforeSetItem(key, value) || [key, value];
      if (newKey !== undefined && newValue !== undefined) {
        originalSetItem.call(this, newKey, newValue);
      }
    };

    storage.getItem = function (key{
      const originalValue = originalGetItem.call(this, key);
      return afterGetItem(key, originalValue);
    };
  }

  unproxy() {
    const { storage, originalSetItem, originalGetItem } = this;
    storage.setItem = originalSetItem;
    storage.getItem = originalGetItem;
  }

  static getInstance(storage = localStorage, config = {}) {
    if (!StorageProxy.instance) {
      new StorageProxy(storage, config);
    }
    return StorageProxy.instance;
  }
}

export default StorageProxy;

注意,我們將 StorageProxy 封裝為單例模式可以確保整個(gè)應(yīng)用中只有一個(gè)實(shí)例被創(chuàng)建和使用。

在 Vue 3 中的調(diào)用示例:

創(chuàng)建一個(gè)單獨(dú)的文件,比如 storageProxy.js

mport StorageProxy from './StorageProxy';

// 配置鉤子函數(shù)
const config = {
  beforeSetItem(key, value) => {
    // ....
    return [key, value];
  },
  afterGetItem(key, value) => {
    // ....
    return value;
  },
};

// 創(chuàng)建單例
const storageProxy = StorageProxy.getInstance(localStorage, config);

export default storageProxy;

在 main.js 中使用單例

將單例注入到 Vue 實(shí)例中,便于全局訪問(wèn):

import { createApp } from 'vue';
import App from './App.vue';
import storageProxy from './storageProxy';

const app = createApp(App);

// 注入全局屬性,供組件使用
app.config.globalProperties.$storageProxy = storageProxy;

app.mount('#app');

總結(jié)

本文給大家介紹了通過(guò)代理 localStorage 和 sessionStorage 實(shí)現(xiàn)自定義存儲(chǔ)邏輯,滿足特定業(yè)務(wù)需求、全局監(jiān)控和數(shù)據(jù)保護(hù)等場(chǎng)景。

核心思路是重寫原生的 setItem 和 getItem 方法,并通過(guò)鉤子函數(shù)提供靈活的定制功能,例如加密存儲(chǔ)、解密讀取和操作攔截。

相信大家一定有所有收獲,快應(yīng)用到自己的項(xiàng)目中吧!

作者:石小石Orz

原文地址:https://juejin.cn/post/7443658721600897035


該文章在 2024/12/11 11:25:51 編輯過(guò)
關(guān)鍵字查詢
相關(guān)文章
正在查詢...
點(diǎn)晴ERP是一款針對(duì)中小制造業(yè)的專業(yè)生產(chǎn)管理軟件系統(tǒng),系統(tǒng)成熟度和易用性得到了國(guó)內(nèi)大量中小企業(yè)的青睞。
點(diǎn)晴PMS碼頭管理系統(tǒng)主要針對(duì)港口碼頭集裝箱與散貨日常運(yùn)作、調(diào)度、堆場(chǎng)、車隊(duì)、財(cái)務(wù)費(fèi)用、相關(guān)報(bào)表等業(yè)務(wù)管理,結(jié)合碼頭的業(yè)務(wù)特點(diǎn),圍繞調(diào)度、堆場(chǎng)作業(yè)而開(kāi)發(fā)的。集技術(shù)的先進(jìn)性、管理的有效性于一體,是物流碼頭及其他港口類企業(yè)的高效ERP管理信息系統(tǒng)。
點(diǎn)晴WMS倉(cāng)儲(chǔ)管理系統(tǒng)提供了貨物產(chǎn)品管理,銷售管理,采購(gòu)管理,倉(cāng)儲(chǔ)管理,倉(cāng)庫(kù)管理,保質(zhì)期管理,貨位管理,庫(kù)位管理,生產(chǎn)管理,WMS管理系統(tǒng),標(biāo)簽打印,條形碼,二維碼管理,批號(hào)管理軟件。
點(diǎn)晴免費(fèi)OA是一款軟件和通用服務(wù)都免費(fèi),不限功能、不限時(shí)間、不限用戶的免費(fèi)OA協(xié)同辦公管理系統(tǒng)。
Copyright 2010-2025 ClickSun All Rights Reserved