Getting Started with Vuex

Vue:2.6

Vuex:3.6.2

Vuex 是專門為 Vue.js 開發的 狀態管理模式(State Management Pattern and Library),它會集中式儲存並管理應用所有元件的狀態,並且以相對應的規則去操作狀態。再更簡單一點來說,就是建立一個單向的資料流,讓狀態變化是可預測的。

舉例來說,以電商的購物車功能為例,各個頁面都需要被共享到目前購物車的細節,這時候就可以透過 Vuex 來實作共享狀態。

另外, VuexEvent bus 差別是,Event bus 是類似全域事件的溝通,可以透過 propsemit 進行資料的傳遞,也就是說,這些事件是需要透過元件間的觸發才能運作;而 Vuex 比較接近是狀態的共享和管理,讓各個元件之間可以同步一些全域的狀態。

Lifecycle

Lifecycle of Vuex
(圖片來源:itread01

Vuex 的 Lifecycle 裡有三個重要的機制需要知道。

  • State: 儲存狀態的物件,使用 單一狀態樹(Single State Tree) 的方式來存放。可以透過 Vuex Getters 取得狀態。
  • MutationsVuex 中改變狀態的唯一方法,通常都是用來實作狀態最原始的更動。
  • Actions:可以用來呼叫 Mutations 的方法,可以支援非同步呼叫(Asynchronous),通常用來實作商業邏輯。

Installation

透過 npm 進行安裝便可以直接使用。

npm install vuex --save
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex);

另外,因為 Vuex 依賴 Promise,所以 IE 瀏覽器 下是跑不動的,我們需要再安裝 es6-promise 套件並引用。

npm install es6-promise --save
import 'es6-promise/auto';

Basic Usage

Vuex 是用 Store 來當作儲存狀態的單位,因此一開始我們可以建立一個 store 的目錄開始實作。

src/store/index.js

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

Vue.use(Vuex);

const store = new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    increment (state) {
      state.count++;
    }
  },
  actions: {
    // 傳入一個 Store instance
    increment (context) {
      // 呼叫 mutations.increment
      context.commit('increment');
    }
  },
});

export default store;

接著在主程式引用 store
src/app.js

import Vue from 'vue';

export default new Vue({
  el: '#app',
  router,
  store
});

最後就可以在 Components 裡面取得相關資料啦。

Components

<template>
  <div class="home">
    <button @click="incrementByCommit()">+1 by commit</button>
    <button @click="incrementByDispatch()">+1 by dispatch</button>
  </div>
</template>

<script>
export default {
  mounted() {
    // 可以透過 this.$store.state 取得 state
    console.log(this.$store.state.count);
  },
  methods: {
    incrementByCommit() {
      // 透過提交 Mutation 的方式更改狀態
      this.$store.commit('increment');
      console.log(this.$store.state.count);
    },
    incrementByDispatch() {
      // 透過呼叫 Action 的方式更改狀態
      this.$store.dispatch('increment');
      console.log(this.$store.state.count);
    }
  }
}
</script>

Getters

Vuex 提供的 Getters 功能,類似元件中的 Computed 機制,每當狀態改變時才會進行計算。這樣一來各個元件在使用特定狀態時,就不需要每一個元件都需要寫一樣的 Computed,只要在 Store 裡實作 Getters 就好了。

比方說,我們可以透過 Getters 實作找出女性會員的資訊。

src/store/index.js

const store = new Vuex.Store({
  state: {
    members: [
      { id: 1, name: 'JohnsonLu', gender: 'male' },
      { id: 2, name: 'Tina', gender: 'female' },
      { id: 3, name: 'Yolanda', gender: 'female' },
    ]
  },
  getters: {
    females: state => {
      return state.members.filter(members => members.gender == 'female');
    },
    // 也可使用其他 getters 當成參數使用
    memberCount: (state, getters)=> {
      return getters.females.length;
    }
  }
});

Components

export default {
  mounted() {
    // [{ id: 2, name: 'Tina', gender: 'female' }, { id: 3, name: 'Yolanda', gender: 'female' }]
    console.log(this.females);
  },
  computed: {
    females () {
      return this.$store.getters.females;
    }
  }
}

另外,可以透過 mapGetters 來取得 Getters,可以簡化程式碼。

Components

import { mapGetters } from 'vuex';

export default {
  computed: {
    ...mapGetters([
      'females',
      'memberCount'
    ]),
  }
}

Vuex 中,分別有 mapStatemapMutationsmapActionsmapGetters 可以使用。

Modules

在大型專案裡,Store 可能會變得很肥,因此 Vuex 也支援 Modules 功能,可以將 Store 拆成數個 Modules,讓程式比較好維護。

src/store/index.js

// Module A
const moduleA = {
  state: {
    count: 0
  },
  mutations: {},
  actions: {},
  getters: {}
}

// Module B
const moduleB = {
  state: {
    name: 'JohnsonLu'
  },
  mutations: {},
  actions: {}
}

const store = new Vuex.Store({
  // 引入 Modules
  modules: {
    a: moduleA,
    b: moduleB
  }
});

之後便可以透過 store.state.a.countstore.state.b.name 取得相對應的值。

正常狀況下,Store 裡的資訊都是屬於 global namespace 的,大家共用一樣的 Actions/Mutations。但如果想要有更高度的封裝性和結構的話,也可以開啟 Namespacing 功能,讓狀態們同時獲得 Namespace

src/store/index.js

const store = new Vuex.Store({
  // 引入 Modules
  modules: {
    // Person Module
    person: {
      // 開啟 Namespacing
      namespaced: true,
      state: { age: 18 }, // state.person.age

      // 引入子 Modules
      modules: {
        company: {
          state: { title: 'Engineer' } // state.person.company.title
        }
      },
    }
  }
});

關於 Modules 還有許多更細節的操作,可以再參考 Vuex Modules 的說明。

Categories: Vue