project from jirengu.com
Vuex--全局状态管理
- state 存放数据的地方
- mutation 用于写数据的地方,同步数据
- action 用于调用method,异步操作,发AJAX请求
Vuex的常用方法:
//index.ts
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
const store = new Vuex.Store({
//给它构造参数,常用的是state和mutations
state:{
count:0;
tag:undefined;
},
mutations:{
addCount(state){
state.count
}
}
})
export default store;
- 在组件中使用Vuex来读写数据:
//money.vue
import {Component} from 'vue-property-decorator';
import store from '@/store/index.ts';
@Component({
components: {Tags, FormItem, Types, NumberPad},
computed: {
recordList() {
return this.$store.state.count; //对象语法,读数据
}
}
...
export default class Money extends Vue {
created(){
this.$store.commit('fetchRecords') //写数据,可以传一个参数payload
}
get count(){
//JS或TS类语法,读数据
}
setTag(){
store.state.tag //获取commit的返回值,读数据
}
}
})
使用Vuex重构`Money.vue
1. 用Vuex对项目代码store
进行重构:
- 安全删除
store/index2.ts
,有多个文件用到此文件内容 - 分别去用到的文件中做暂时修改,赋空值或直接注释掉,标记
//TODO
,直到yarn serve
页面控制台不报错为止,接下来再分别用Vuex重构以上的组件。 - 让
recordList
一开始就有数据,recordList
初始化:
//store.index.ts
import Vue from 'vue';
import Vuex from 'vuex';
import clone from '@/lib/clone';
Vue.use(Vuex);
const store = new Vuex.Store({
state: {
recordList:[] as RecordItem[]
},
mutations: { //不需要return
fetchRecords(state) { //`recordList`初始化
state.recordList = JSON.parse(window.localStorage.getItem('recordList') || '[]') as RecordItem[];
},
createRecord(state,record){
const record2: RecordItem = clone(record);
record2.createdAt = new Date();
state.recordList.push(record2);
console.log(state.recordList);
// TO DO
// recordStore.saveRecord();
},
saveRecord(state) {
window.localStorage.setItem('recordList', JSON.stringify(state.recordList));
},
}
});
export default store;
- 只需要
created
的时候,调用fetchRecords
即可获取到用户添加的数据
//Money.vue
created(){
this.$store.commit('fetchRecords')
}
- 保存用户添加的数据:
//index.ts
...
createRecord(state, record) {
const record2: RecordItem = clone(record);
record2.createdAt = new Date();
state.recordList.push(record2);
store.commit('saveRecord'); //保存用户添加的数据
使用Vuex重构`Tags.vue
- 第一步,读取tag列表:
//Tags.vue
@Component({
computed: {
tagList() {
return this.$store.state.tagList;
}
}
})
- 第二步,声明
tagList
,并初始化:
//store/index.ts
const store = new Vuex.Store({
state: {
recordList: [] as RecordItem[],
tagList:[] as Tag[] //声明tagList
},
mutations:{
fetchTags: function (state) { //初始化
return state.tagList = JSON.parse(window.localStorage.getItem('tagList') || '[]');
}
}
}
- 第三步,使用
tagList
:
//Tags.vue
export default class Tags extends Vue {
created() {
this.$store.commit('fetchTags');
}
}
- 第四步,把原来的
tagStore
中的createTag
和saveTags
粘贴到index.ts
中:
//
const store = new Vuex.Store({
state: {
recordList: [] as RecordItem[],
tagList: [] as Tag[]
},
mutations: {
createTag(state, name: string) {
const names = state.tagList.map(item => item.name);
if (names.indexOf(name) >= 0) {
window.alert('标签名重复了');
return 'duplicated';
}
const id = createId().toString();
state.tagList.push({id, name: name});
store.commit('saveTags');
window.alert('添加成功');
return 'success';
},
saveTags(state) {
window.localStorage.setItem('tagList',
JSON.stringify(state.tagList));
}
}
}
- 第五步,在
Tags.vue
中调用create
:
create() {
const name = window.prompt('请输入标签名');
if (!name) {
return window.alert('标签名不能为空');
}
this.$store.commit('createTag', name);
}
使用Vuex重构`Labels.vue
@Component({
components: {Button},
computed: {
tags() { //新增
return this.$store.state.tagList;
}
}
})
export default class Labels extends Vue {
beforeCreate() { //新增fetch,每次点击Labels时都fetch最新
this.$store.commit('fetchTags');
}
createTag() { //与Money.vue中的create()相同的代码,可以用mixin
const name = window.prompt('请输入标签名');
if (!name) {
return window.alert('标签名不能为空');
}
this.$store.commit('createTag', name);
}
}
-
用
mixin
来优化createtag
,把多个组件用到的方法归拢到一个mixins组件,然后用到的时候直接继承: -
官方文档为:Vue Class Component
-
直接把
mixin
声明为组件
//TagHelper.ts
import Vue from 'vue'
import Component from 'vue-class-component'
@Component
export class TagHelper extends Vue {
createTag() {
const name = window.prompt('请输入标签名');
if (!name) {
return window.alert('标签名不能为空');
}
this.$store.commit('createTag', name);
}
}
export default TagHelper;
- 在
labels.vue
中引入这个mixin
组件:
import {mixins} from 'vue-class-component';
const tagHelper:any = require('@/mixins/TagHelper') ;
@Component({
components: {Button},
mixins:[tagHelper],
computed: {
tags() {
return this.$store.state.tagList;
}
}
export default class Labels extends mixins(TagHelper) {
beforeCreate() {
this.$store.commit('fetchTags');
}
}
})
- 在
Tags.vue
中引入这个mixin
组件:
<button @click="createTag">添加标签</button>
export default class Tags extends mixins(TagHelper) {
}
使用Vuex重构`EditLabel.vue
store.commit没有返回值,通过在store上添加一个currentTag来获取commit('setCurrentTag')的返回值
- 先声明数据类型:
//store/index.ts
type RootState = {
recordList: RecordItem[],
tagList: Tag[],
currentTag?: Tag
}
const store = new Vuex.Store({
state: {
recordList: [] ,
tagList: [] ,
currentTag:undefined
} as RootState,
- 用原生自带的
set
和get
,基于多次尝试如下代码无法读取到数据,比如Tags.vue
中:
//Tags.vue
//此组件只是没有在下面引用到tagList;其实computed部分可以删掉,然后直接在下面用get tagList就行。
@Component({
computed: {
tagList() {
return this.$store.state.tagList;
}
}
})
export default class Tags extends mixins(TagHelper) {
- 再比如在
Labels.vue
中也存在这个情况:
//Labels.vue
@Component({
components: {Button},
computed: { //可以删掉
tags() { //可以删掉
return this.$store.state.tagList;
}
}
})
export default class Labels extends mixins(TagHelper) {
}
- 按照上面的思路去改造之前的写法:
//Tag.vue
@Component({}) //删掉之前的computed
//Labels.vue
@Component({
components: {Button},
})
export default class Labels extends mixins(TagHelper) {
get tags() {
return this.$store.state.tagList; //新增内容
}
//EditLabel.vue
export default class EditLabel extends Vue {
get tag() {
return this.$store.state.currentTag;
}
//Money.vue
@Component({
components: {Tags, FormItem, Types, NumberPad},
})
export default class Money extends Vue {
get recordList() {
return this.$store.state.recordList;
}
继续重构`EditLabel.vue
mutations里面的函数只能接受两个参数
- 实现修改标签名:
//index.ts
updateTag(state, payload: { id: string, name: string }) { //只能接受两个参数
const {id, name} = payload;
const idList = state.tagList.map(item => item.id);
if (idList.indexOf(id) >= 0) {
const names = state.tagList.map(item => item.name);
if (names.indexOf(name) >= 0) {
window.alert('标签名重复了')
} else {
const tag = state.tagList.filter(item => item.id === id)[0];
tag.name = name;
store.commit('saveTags');
}
}
},
//EditLabel.vue
update(name: string) {
if (this.tag) {
this.$store.commit('updateTag',
{id:this.tag.id,name});
}
}
- 实现删除功能
//index.ts
removeTag(state, id: string) {
let index = -1;
for (let i = 0; i < state.tagList.length; i++) {
if (state.tagList[i].id === id) {
index = i;
break;
}
}
state.tagList.splice(index, 1);
store.commit('saveTags');
},
//EditLabel.vue
remove() {
if (this.tag) {
this.$store.commit('removeTag',this.tag.id);
}
}