vue全局数据管理
不吃辣的小周 人气:0记账页面标签页面新增
记账页面和标签页面都可以新增标签。可是现在有一个bug。在标签页面新增标签之后,在记账页面不会自动同步,要刷新一下才能同步。
这是因为这两个页面的数据tagList都是分别从tagListModel里fetch的。所以就导致了数据不同步。
解决方案:在更高一层的地方统一去tagListModel里fetch一次,把取出来的设成一个全局的属性,然后分别在两个页面直接使用。
我们选择在main.ts入口文件里声明这个全局属性:
window.tagList=tagListModel.fetch()
可是这样会报错,说window里没有这样一个属性。
那么我们就只能在custom.d.ts里自定义声明window里有这样一个属性。
interface Window { tagList: Tag[] }
这样再回到main.ts,就没有报错了。
然后就可以在两个页面直接使用。
在Money.vue:
export default class Money extends Vue{ tags=window.tagList }
在Labels.vue:
export default class Labels extends Vue { tags=window.tagList }
这样就没有那个bug了。在标签页面新增一个标签后,在记账页面也会自动同步。
把数据放到window上以后,读取数据就是通过window操作的。但是写数据,比如删除,增加,修改标签,还是通过tagListModel这个对象的API操作的。
将API封装到window
那么为了看起来一致,我们最好把这些API也封装到window上。
//main.ts //record store window.recordList=recordListModel.fetch() window.createRecord=(record: RecordItem)=>{ recordListModel.create(record) } // tag store window.tagList=tagListModel.fetch() window.createTag=(name: string)=>{ const message=tagListModel.create(name) if(message==='duplicated'){ window.alert('标签名重复') }else{window.alert('添加成功')} } window.removeTag=(id: string)=>{ tagListModel.remove(id) } window.updateTag=(id: string,newName: string)=>{ return tagListModel.update(id,newName) } window.findTag=(id: string)=>{ return window.tagList.filter(tag=>tag.id===id)[0] }
把recordListModel,tagListModel上的属性,方法都封装到window上以后,有一个问题。
1. window上的变量太多了。
2. 太依赖window了,如果在不支持window的情况下,就无法操作。
解决办法: 不要window,把这些API都封装到一个store对象上。在store目录里新建一个index2.ts
const store={ //record store recordList: recordListModel.fetch(), createRecord: (record: RecordItem) => { recordListModel.create(record); }, // tag store tagList: tagListModel.fetch(), createTag: (name: string) => { const message = tagListModel.create(name); if (message === 'duplicated') { window.alert('标签名重复'); } else { window.alert('添加成功'); } }, removeTag: (id: string) => { tagListModel.remove(id); }, updateTag: (id: string, newName: string) => { return tagListModel.update(id, newName); }, findTag: (id: string) => { return store.tagList.filter(tag => tag.id === id)[0]; } } export default store;
以后操作数据就全部调用store
可是这样store对象里有两种数据的操作,我们最好还是把他们分开:
import recordStore from '@/store/recordStore'; import tagStore from '@/store/tagStore'; const store={ ...recordStore, ...tagStore } export default store;
分成recordStore和tagStore两个文件。然后统一在store里浅复制
那么全局数据管理的好处是什么?
- 解耦:将所有数据相关的逻辑放入 store(也就是 MVC 中的 Model,换了个名字而已)。即从localStorage里取数据,存数据,对数据增删改查,都由store里的API提供,别人要对数据操作,直接调用API就可以,不需要关心里边的逻辑。
- 数据读写更方便:任何组件不管在哪里,都可以直接读写数据。比如说,原来Tags子组件里的标签列表是由它的父组件Money传进去的,如果它要增加标签,不能自己增加,要把数据传出去在外边修改。现在做了全局数据管理之后,Tags子组件完全可以自己直接调用store,读写数据,不需要由父组件操作。
- 控制力更强:组件对数据的读写只能使用 store 提供的 API 进行(当然也不排除有猪队友直接对 tagList 和 recordList 进行 push 等操作,这是没有办法禁止的) 前提:
我们在Money.vue里,通过recordList=store.recordList
,这个data是从存储库里取出来的,他是一个数组,所以他们共用一个地址。所以我在store里对store.recordList进行增删改查时,我的vue实例的数据recordList也会随着改变。
但是如果store里的数据不是对象,而是基本数据类型的数据呢?
我们知道,如果是基本数据类型,如字符串,数字等,传递时是复制值的。
如:如果在store里声明一个数字,
const store={ count:0, addCount(){ this.count+=1 }, ...recordStore, ...tagStore }
然后在Money里,
export default class Money extends Vue{ count=store.count add(){ store.addCount() console.log(store.count); }
把store.count赋值给自己的data,然后点击一个按钮调用add。发现页面里展示的count,没有变,还是0.
因为这是值传递,改变的只是store里的count。
那怎么做能同步呢?使得修改store.count,我这个实例上的count也会跟着改变呢?
不要用data获取,因为data只会获取一遍,后边的就不会再更新了
用computed计算属性
@Component({ components: {FormItem, Type, Tags, NumPad}, computed:{ count(){ return store.count } } })
在ts里,computed写在@Component里。
我这个count是一个计算属性,他的值是返回store.count的值。但是点击之后,页面上的0还是没有加1 .
因为Vue并没有监听store对象,所以改变store,Vue并不知道。那就把store写在data里,
import store from "@/store/index2.ts" export default{ data(){ return { store:store, } } }
为了不重复,我们把它放在App.vue里,这样Vue就监听了store对象。
这样做了以后,点击按钮,成功加1.
之后为了方便,不管是对象,还是基本的数据类型,我们都放在计算属性里,然后全局监听store对象。
加载全部内容