需求背景
旧项目在跳转到登录页时,会清空浏览器中的所有本地存储(SessionStorage和LocalStorage),从而影响到该域名下的其他应用。
解决方案
解决方案很简单,只要给项目使用到的Storage附加唯一命名空间,并且在登录页的时候只清空当前命名空间下的Storage存储。
分析现状
- 项目中封装了
RootStorage,特性请看【旧api】 - 存在部分数据直接调用
Storage原生api进行读取操作,且没有命名空间标识 - 部分存储使用了
RootStorage进行管理,但存在多个命名空间 - 项目中频繁调用
new RootStorage(namespace)创建新的实例,其实做的都是相同的操作 - 有些数据如
projectId,需要同时存储在LocalStorge和SessionStorage时,就出现了一些别扭的代码逻辑来处理,如下:
总结:经过分析,一部分原因是使用不规范,另一部分原因是RootStorage设计不合理。
RootStorage旧api设计
/**
* doc:
* RootStorage {
* name // nameSpace
* type // 'local'/'session'
* }
*
* const myLocalStorage = new RootStorage({ name: 'my', type: 'local'})
* const mySessionStorage = new RootStorage({ name: 'my', type: 'session'})
*
* myLocalStorage.set('key', 'value')
* myLocalStorage.set({ key1: 'value1', key2: 'value2'}) // 解构
* myLocalStorage.set(['value1', 'value2']) // 解构
* myLocalStorage.get('aa')
* myLocalStorage.remove('aa')
* myLocalStorage.clear() // 清空当前实例
* myLocalStorage.clear(true) // 清空整个loalstorage
*
* mySessionStorage.set('key', 'value')
* mySessionStorage.get('aa')
* mySessionStorage.remove('aa')
* mySessionStorage.clear() // 清空当前实例
*
*/
功能特性:
- 封装了
RootStorage来管理storage的存储 - 支持
get操作的数据parse - 支持
set操作的解构 - 支持设置命名空间
- 支持清空【某种类型(local/session)&&某个命名空间】下的所有的存储数据
RootStorage新api设计
/**
* const mystorage = RootStorage.createStorage('my') // my:
* const myDevStorage = mystorage.createStorage('dev') // my:dev:
* const myDevOfflineStorage = myDevStorage.createStorage('offline') // my:dev:offline:
* mystorage.setLocal('key', 'value')
* mystorage.getLocal('key')
* mystorage.removeLocal('key')
* mystorage.setSession('key', 'value')
* mystorage.getSession('key')
* mystorage.removeSession('key')
* mystorage.set('key', 'value') // all
* mystorage.get('key') // session > local
* mystorage.remove('key')
* mystorage.clear() // 清空当前命名空间下的所有storage存储
*/
功能特性:
- 将命名空间的层级做了提升,创建实例时,作为唯一且必须的参数,且不能重复;
- 一个命名空间下存在多种类型的存储,包括
localStorage,sesstionStorage... - 一个项目应该有一个项目级的nameSpace,如 name:
- 项目中模块支持子命名空间,如 name:subName:
- 支持清空【某个命名空间】下的所有的存储数据
总结:重新设计后的api明显更能贴合实际的应用场景,且使用起来更方便便捷。
但是因为是稳定的版本,怕改动太多存在风险,所以最终采用了另一种曲线救国的方法。。
曲线救国
LocalStorage和SessionStorage都是Storage类的实例
getItem和setItem都是Storage原型上的方法,我们直接往原型上添加新的方法,该方法将附加上指定的命名空间,具体实现如下:
// 注入 getSanbaItem,setSanbaItem 方法
const nameSpace = 'sanba:';
Storage.prototype.getSanbaItem = function (keyName, ...args) {
const realKeyName = `${nameSpace}${keyName}`;
return this.getItem(realKeyName, ...args);
};
Storage.prototype.setSanbaItem = function (keyName, ...args) {
const realKeyName = `${nameSpace}${keyName}`;
this.setItem(realKeyName, ...args);
};
Storage.prototype.removeSanbaItem = function (keyName, ...args) {
const realKeyName = `${nameSpace}${keyName}`;
this.removeItem(realKeyName, ...args);
};
// 清空 datasanba 下的所有本地存储
export function clearSanbaStorage() {
for (let i in window.localStorage) {
if (Object.hasOwnProperty.call(window.localStorage, i)) {
if (i.indexOf(nameSpace) === 0) {
window.localStorage.removeItem(i);
}
}
}
for (let i in window.sessionStorage) {
if (Object.hasOwnProperty.call(window.sessionStorage, i)) {
if (i.indexOf(nameSpace) === 0) {
window.sessionStorage.removeItem(i);
}
}
}
}
大功告成,接下来只需要全局将getItem,setItem,clear方法替换掉即可,风险可控。