vue.js开发记账(8)

44 阅读2分钟

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:0tag: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中的createTagsaveTags粘贴到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,
  • 用原生自带的setget,基于多次尝试如下代码无法读取到数据,比如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);
    }
}