共计 3887 个字符,预计需要花费 10 分钟才能阅读完成。
需求:
几乎所有的 web 应用都需要保存数据一些到本地,那么我们就来做一个数据储存器吧。
详细需求:
当本地储存有数据时,取用本地的数据,没有时使用默认的数据
判断本地的数据是否过时,如果过时则不使用
默认使用 localStorage,但支持使用其它储存方式,并且要支持多方储存,多方读取
抽象出对象
根据需求里面的关键字,我们抽象出三个对象:数据访问、数据、储存器
数据储存管理器负责管理数据,对外暴露接口
数据对象负责对数据的操作
储存器负责保持数据,读取数据
储存器对象
DataStorageManagerBase 暴露两个接口 save() 和 load(), 模拟抽象类,告诉子类一定要实现这两个方法。
下面的例子用 LocalStorage 实现了一个子类,当然你也可以用 cookie 或其它方式实现。
为什么要把 LocalStorage 这些储存器进行二次封装呢?直接用不就可以了吗?
因为各种储存器等 api 都是不一样等,我们二次封装后可以确保无论什么储存器对外暴露的接口都是 save() 和 load()。
/* 模拟数据储存器抽象类,其实这个类不要也可以 */
class DataStorageManagerBase {static getIns() {
/* 储存器在整个应用的生命周期里应该只有一个实例 */
if (!this._ins) this._ins = new this();
return this._ins;
}
constructor() {this.name = null;}
save(name/* string */, data/* any */) {throw '"'+ this.constructor.name +"' 类没有 save() 方法 ";
}
load(name/* string */) {throw '"'+ this.constructor.name +"' 类没有 load() 方法 ";
}
}
class LocalStorageManager extends DataStorageManagerBase {static getIns() {
/* 静态方法不能继承 */
if (!this._ins) this._ins = new this();
return this._ins;
}
constructor() {super();
this.name = 'localStorage';
}
save(name/* string */, data/* any */) {console.log(name,data)
if (!window.localStorage) return this;// 判断这个储存器可用不可用,你也可以在这里抛出错误
window.localStorage[name] = JSON.stringify(data);
return this;
}
load(name/* string */) {
// 如果储存器不可用,返回 false
if (!window.localStorage) return false;
// 如果没有这个数据,返回 false
if (!window.localStorage[name]) return false;
let dataLoaded = JSON.parse(window.localStorage[name]);
return dataLoaded;
}
}
数据对象
对数据的操作:保存、读取、判断版本等
class GlobalData {static addStorage(storage/* DataStorageManagerBase */) {
/* 动态添加储存器 */
this._storages.push();}
static getStorages() {return this._storages;}
constructor(name, data, version) {
this.name = name;
this.data = data;
this.version = version || 1;
this._loadData();
// 初始化的该对象的时候,读取 localStorage 里的数据,看有没有已储存的数据,有就用该数据
}
getData() {return this._copy(this.data);
}
setData(data, notSave) {this.data = this._copy(data);
if (!!notSave) return this;
let dataToSave = {
name: this.name,
version: this.version,
data: this.data
};
let storages = GlobalData.getStorages();
for (let i = 0, l = storages.length; i < l; i++) {
/* 轮询所有储存器,把数据保存在这些储存器中 */
storages[i].save(this.name,dataToSave);
}
return this;
}
_copy(data) {
/* 深拷贝 */
if (typeof data != "object") return data;
return JSON.parse(JSON.stringify(data));
}
_loadData() {let storages = GlobalData.getStorages();
for (let i = 0, l = storages.length; i < l; i++) {
/* 轮询所有储存器,依次获取数据 */
const dataLoaded = storages[i].load(this.name);
if(!!dataLoaded) {this._updateData(dataLoaded);
return;
}
}
}
_updateData(dataLoaded) {if (dataLoaded.version < this.version) return this;
this.data = dataLoaded.data;
return this;
}
}
GlobalData._storages = [LocalStorageManager.getIns()];// 默认添加 LocalStorageManager 储存器
数据访问对象
对数据对象管理, 对外暴露三个接口 getData(),setData(),config(), 用户通过这三个接口使用这个模块
class GlobalDataDao {static getIns() {if (!this._ins) this._ins = new this();
return this._ins;
}
constructor() {
this.GlobalDataClass = GlobalData;
this._dataList = [];}
getData(name/* string */) {let dataIns = this.getDataIns(name);
if (!!dataIns) {return dataIns.getData();
} else {return null;}
}
setData(name/* string */, data/* any */, notSave = false/* ?boolean */) {let dataIns = this.getDataIns(name);
dataIns.setData(data, notSave);
return this;
}
config(configs/* Array */) {
/* 初始化数据
interface Config {
name: string;
data;any;
version?: number;
}
*/
for (let i in configs) {let de = configs[i];
if (!!this.getDataIns(de.name)) {
/* 如果数据名重复,抛出错误 */
throw new Error('data name"' + de.name + '"is exist');
};
let dataIns = new GlobalData(de.name, de.data, de.version);
this._dataList.push(dataIns);
}
return this;
}
getDataIns(name/* string */) {for (let i in this._dataList) {if (this._dataList[i].name === name) {return this._dataList[i];
}
}
return false;
}
}
使用
/* 用法 */
let globalDataManeger = GlobalDataDao.getIns();
let configs = [
{
name: 'languageUsing',
version: 1,
data: {
name: '简体中文',
value: 'zh-cn'
}
}, {
name: 'userPreference',
version: 1,
data: {
theme: 'blue',
menu: 'side_bar'
}
}
];
globalDataManeger.config(configs);
console.log(globalDataManeger);
let languageUsing = globalDataManeger.getData('languageUsing');
console.log(languageUsing);
languageUsing.name = 'English';
languageUsing.value = 'en';
globalDataManeger.setData('languageUsing', languageUsing);
正文完
星哥玩云-微信公众号