在做完标签页后发现了一个bug,即Label.vue和Money.vue的数据不会同步更新,需要手动刷新才行(如,在标签页新建一个标签,点击记账页发现标签没有自动更新)。这是因为他们俩的数据“各自为政”,即每个组件都会单独对recordListModel进行fetch,如果做到全局数据管理的话,这个问题就迎刃而解了。
重新封装recordListModel
之前我们使用外部传过来的data,现在重新封装recordListModel,使其自身带有data以便操作。同时,将create封装到recordListModel。
import clone from '@/lib/clone';
const localStorageKeyName = 'recordList';
const recordListModel = {
data: [] as RecordItem[],
create(record:RecordItem){
const record2: RecordItem = clone(record);
record2.createdAt = new Date();
this.data.push(record2);
},
fetch() {
this.data = JSON.parse(window.localStorage.getItem(localStorageKeyName) || '[]') as RecordItem[];//强制类型声明
return this.data;
},
save() {
window.localStorage.setItem(localStorageKeyName, JSON.stringify(this.data));
}
};
export default recordListModel;
复制代码
用window来容纳数据
在上层main.ts中进行
window.tagList = tagListModel.fetch();t
复制代码
注意,需要在custom.d.ts中声明tagList,tagListModel.vue中的类型声明可以删除。
type Tag = {
id: string;
name: string;
}
type TagListModel = {
data: Tag[]
fetch: () => Tag[]
create: (name: string) => 'success' | 'duplicated' // 联合类型
update: (id: string, name: string) => 'success' | 'not found' | 'duplicated'
remove: (id: string) => boolean
save: () => void
}
interface Window {
tagList: Tag[]
}
复制代码
这样的话,Label.vue和Money.vue就不需要fetchl,直接使用window.tagList即可。
封装tagListModel
现在的情况是,读数据从window.tagList读,写数据从tagListModelconst message = tagListModel.create(name);
写。没有进行统一,不符合最小知识原则。
window.createTag = (name: string) => {
const message = tagListModel.create(name);
if (message === 'duplicated') {
window.alert('标签名重复了');
} else if (message === 'success') {
window.alert('添加成功');
}
};
复制代码
Label.vue就可以直接使用
createTag() {
const name = window.prompt('请输入标签名');
if (name) {
window.createTag(name);
}
}
复制代码
别忘了在custom.d.ts中声明createTag
createTag:(name:string)=>void
复制代码
其余的就不单独列了
//main.ts
window.tagList = tagListModel.fetch();
window.createTag = (name: string) => {
const message = tagListModel.create(name);
if (message === 'duplicated') {
window.alert('标签名重复了');
} else if (message === 'success') {
window.alert('添加成功');
}
};
window.removeTag = (id: string) => {
return tagListModel.remove(id);
};
window.updateTag = (id: string, name: string) => {
return tagListModel.update(id, name);
};
复制代码
custom.d.ts中声明
interface Window {
tagList: Tag[];
createTag: (name: string) => void;
removeTag: (id: string) => boolean;
updateTag:(id: string, name: string) => 'success' | 'not found' | 'duplicated'
}
复制代码
封装recordListModel
window.recordList = recordListModel.fetch();
window.createRecord = (record: RecordItem) => {
return recordListModel.create(record);
};
复制代码
recordList: RecordItem[];
createRecord: (record: RecordItem) => void
复制代码
问题:全局变量太多,严重依赖window
解决办法:
- 把全局变量全部放在window.store中。(解决全部变量太多)
- 把整个store放在store/index.2中。(解决严重依赖window)
- 因为store的类型会自动推测,所以类型声明可以删除。
import recordListModel from '@/models/recordListModel';
import tagListModel from '@/models/tagListModel';
const store = {
//record store
recordList: recordListModel.fetch(),
createRecord: (record: RecordItem) => {
return recordListModel.create(record);
},
//tag store
tagList: tagListModel.fetch(),
findTag(id: string) {//箭头函数的this是window,所以把这里的箭头函数改掉
return this.tagList.filter(t => t.id === id)[0];
},
createTag: (name: string) => {
const message = tagListModel.create(name);
if (message === 'duplicated') {
window.alert('标签名重复了');
} else if (message === 'success') {
window.alert('添加成功');
}
},
removeTag: (id: string) => {
return tagListModel.remove(id);
},
updateTag: (id: string, name: string) => {
return tagListModel.update(id, name);
},
};
export default store;
复制代码
这时任何地方想用,直接 store.
即可。
注意:箭头函数的this是window,用this就不要用箭头函数
优化:record store和tag store分离
分别在store中新建两个,直接export default,在index2.ts中引用即可。
import recordStore from '@/store/recordStore';
import tagStore from '@/store/tagStore';
const store = {
...recordStore,
...tagStore
};
export default store;
复制代码
全局状态管理的优点
全局状态管理(也叫全局数据管理)的好处是什么?
- 解耦:将所有数据相关的逻辑放入 store(也就是 MVC 中的 Model,换了个名字而已)
- 数据读写更方便:任何组件不管在哪里,都可以直接读写数据
- 控制力更强:组件对数据的读写只能使用 store 提供的 API 进行(当然也不排除有猪队友直接对 tagList 和 recordList 进行 push 等操作,这是没有办法禁止的)
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END