iT邦幫忙

0

[鼠年全馬] 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


尚未有邦友留言

立即登入留言