Vue果果记账-全局数据管理

在做完标签页后发现了一个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

解决办法:

  1. 把全局变量全部放在window.store中。(解决全部变量太多)
  2. 把整个store放在store/index.2中。(解决严重依赖window)
  3. 因为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;
复制代码

全局状态管理的优点

全局状态管理(也叫全局数据管理)的好处是什么?

  1. 解耦:将所有数据相关的逻辑放入 store(也就是 MVC 中的 Model,换了个名字而已)
  2. 数据读写更方便:任何组件不管在哪里,都可以直接读写数据
  3. 控制力更强:组件对数据的读写只能使用 store 提供的 API 进行(当然也不排除有猪队友直接对 tagList 和 recordList 进行 push 等操作,这是没有办法禁止的)
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享