[鼠年全马] W39 - 使用Vuex管理资料状态(下)

这周要继续来探讨 Vuex
上周的文章传送门

首先先回顾一下上周提到的 Store 中有这些东西:

  • state: 存放资料状态的物件,类似Vue元件中的 data
  • actions: 存放方法的物件,类似Vue元件中的 methods
    除了改变资料状态以外的工作,都会在这边处理
  • mutations: 负责做改变资料状态的动作
  • getters: 取得资料状态的方法,在取得之前也可以在这先做一些资料处理,类似Vue元件中的 computed
  • modules: 模组化的Vuex

#getters取得资料

还记得上周取得资料的时候,我们直接使用 $store.statecomputed 中直接抓资料吗?

当资料需要被处理过的话,当然也可以从 computed 中进行处理,例如以上周的 TodoList 例子来说,假设我需要计算有几笔是已完成的,就可以这样写:

computed: {
  countTodoDone() {
    let todolist = this.$store.state.todolist;
    return todolist.filter((item) => {
      return item.done === true;
    });
  },
},

getters 要干嘛xD?

当有多个地方需要用到相同的计算结果时,getters 就会现身~
而他的写法就跟 computed 一样:

getters: {
  countTodoDone() {
    let todolist = this.$store.state.todolist;
    return todolist.filter((item) => {
      return item.done === true;
    });
  },
},

准备好 getters 之後,在元件中就能使用 mapGetters() 的方式来取得计算结果罗~

//元件中先 import 它
import { mapGetters } from "vuex";

export default {
  ...,
  computed: {
    ...mapGetters(["countTodoDone"]),
  },
};

如果有多个 getters 要取得,就直接加在阵列中就好:

...mapGetters(["countTodoDone", "AAA", "BBB"]),

#模组化

在大型专案中,不可能只存在一个元件,当有多个元件都要存取 Store,这时候就会容易发生冲突的状况
例如取了一样的资料名称或是方法名称等等

这时候就可以将 Store 进行模组化 modules ,分割成多个模组来解决这个困境

由於使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。
为了解决以上问题,Vuex允许我们将store分割成模块(module)。

而分割出来的每一个模组都各自包含自己的:

  • state
  • actions
  • mutations
  • getters

官网的模组化范例是这麽写的:

const moduleA = {
  state: () => ({ ... }),
  mutations: { ... },
  actions: { ... },
  getters: { ... }
}

const moduleB = {
  state: () => ({ ... }),
  mutations: { ... },
  actions: { ... }
}

const store = new Vuex.Store({
  modules: {
    a: moduleA,
    b: moduleB
  }
})

store.state.a // -> moduleA 的状态
store.state.b // -> moduleB 的状态

可以看到范例中 moduleAmoduleB 各自有自己的内容,最後放进 Storemodules

不过我的观念是不会把模组和 Store 写在一起,因为这样全挤在一坨很恶心而且不好维护~
那要怎麽写呢?

我们拿昨天的 TodoList 来改改看吧~

#Step 1

首先,在 /store 资料夹里面新增一个档案 - todolist.js

src
└── store
    ├── index.js
    └── todolist.js //新增这个档案

#Step 2

模组中该有的基本内容都加上去:

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default {
    state: { },
    actions: { },
    mutations: { },
    getters: { }
};

注意这边汇出(export)的不是 Store 喔!!
有些菜鸟没注意到 (就是我)

#Step 3

把跟 TodoList 相关的内容全部移植过去:

export default {
    state: {
        todolist: [
            { id: 1, todo: "交女朋友", done: false },
            { id: 2, todo: "财务自由", done: false },
            { id: 3, todo: "当码农", done: true },
        ],
    },
    actions: {
        todoDone(context, { id, done }) {
            context.commit('TODOLIST', { id, done });
        },
    },
    mutations: {
        TODOLIST(state, { id, done }) {
            state.todolist.find((item) => {
                return item.id === id
            }).done = done;
        },
    },
    getters: {
        countTodoDone() {
            let todolist = this.$store.state.todolist;
            return todolist.filter((item) => {
                return item.done === true;
            });
        },
    }
};

#Step 4

/store/index.js 主档中引用 todolist.js:

import TodoListModules from './todolist'

并且加进 modules:

export default new Vuex.Store({
    ...,
    modules: {
        RoomsModules,
        TodoListModules,
    },
});

#Step 5

开启 App.vue 在取得 Store 资料的部分要稍做修改

  • myTodo 加上模组的部份
computed: {
  myTodo() {
    return this.$store.state.TodoListModules.todolist;
  },
},

到这边暂停一下,这里模组化的内容中
state 是注册在 TodoListModules 命名空间中
而其他的 actions & mutations & getters 则是注册在全局命名空间

所以会发现就算 todoDone() 不加上模组也能呼叫:

methods: {
  todoDone(id, done) {
    this.$store.dispatch("todoDone", { id, done });
  },
},

#Step 6

官方是这麽说明的:

默认情况下,模块内部的action、mutation和getter是注册在全局命名空间的——这样使得多个模块能够对同一mutation或action作出响应。
如果希望你的模块具有更高的封装度和复用性,你可以通过添加namespaced: true的方式使其成为带命名空间的模块。
当模块被注册後,它的所有getter、action及mutation都会自动根据模块注册的路径调整命名。

意思就是如果想让模组完整度高一点,可以加上 namespaced: true 这个属性,我们来加看看吧~

// /store/todolist.js
export default {
    namespaced: true,
    ...,
};

这时候点清单会发现console跳错罗!!

///[vuex] unknown action type: todoDone

回到 todoDone() 来加上模组,就又正常罗~

methods: {
  todoDone(id, done) {
    this.$store.dispatch("TodoListModules/todoDone", { id, done });
  },
},

到这就大功告成~ 完美移植TodoList模组化~
可以看一下画面一样可以动 哈哈xD
gif已死QQ


目前我研究到这而已~
已经可以把很多现有的专案改良了!!

其他功能,之後如果有再深入探讨的话再来发一篇吧~
/images/emoticon/emoticon29.gif/images/emoticon/emoticon29.gif/images/emoticon/emoticon29.gif


<<:  JS Promise DAY77

>>:  [ Day 36 ] - Electron 应用程序 - 更新自动化 ( 实做篇 )

NNI执行的流程

一个Experiment的运行逻辑是: • Tuner 接收搜索空间,生成configuration...

Extra01 - glob - 配置目标档案与目录

此为番外,此篇选入番外的原因是 glob 并不是个工具,但是是个会常被各种工具采用的一种配置方式。...

Day27 用python写UI-聊聊Treeview(一)

终於结束Text的部分啦~~~ 今天要来讲Treeview,这个就是树状的意思,像树一样有层次感,可...

【Day 15】Function 函式

前言 函式用於结构化程序,将需要重复用到的功能独立出来,透过函式的呼叫,传入资料与回传处理後的资料。...

Day 1:为什麽工程师要建立自己的技术部落格?

大家好我是 Gui,一名刚於私立科大资管系毕业的社会新鲜人,这是我第一次参与 IT 铁人赛,既紧张又...