vuex 初體驗


從最簡單的範例開始學習,配合官方網站文件使用

Vuex - passing multiple parameters to action

基本狀態管理

下面範例從官網取得,主要演示了基本的 state 及 mutations 操作

const store = new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    increment: state => state.count++,
    decrement: state => state.count--
  }
});

new Vue({
  el: "#app",
  store,
  computed: {
    count() {
      return this.$store.state.count;
    }
  },
  methods: {
    increment() {
      store.commit("increment");
    },
    decrement() {
      store.commit("decrement");
    }
  }
});
  1. state 用來存放需要被共用的資料,這裡面的資料需要異動的話,需要透過 mutations 來更新資料
  2. store 狀態更新:store.commit('方法名稱',傳入參數)
  3. mutations 第一個參數為 state,第二個參數為傳入的參數,若有多個參數需要傳入,可透過解構來處理
  4. 透過計算屬性取得 store 狀態
  5. 在根組件註冊 store,子組件可透過this.$store訪問到數據

getter 的用法

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Document</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/vuex/3.1.0/vuex.js"></script>
  </head>
  <body>
    <div id="app">
      <ul>
        <li v-for="(item,index) in list" :key="index">{{ item }}</li>
      </ul>
      共 {{ listCount }} 筆資料
      <button @click="getCountryList">getCountryList</button>
      <button @click="getNumberList">getNumberList</button>
    </div>

    <script>
      const ENUMS = {
        List: {
          Number: "GetNumberList",
          Country: "GetCountryList"
        }
      };

      function postData(url, data) {
        // Default options are marked with *
        return fetch(url, {
            body: JSON.stringify(data), // must match 'Content-Type' header
            cache: "no-cache", // *default, no-cache, reload, force-cache, only-if-cached
            credentials: "same-origin", // include, same-origin, *omit
            headers: {
                "content-type": "application/json"
            },
            method: "POST", // *GET, POST, PUT, DELETE, etc.
            mode: "cors", // no-cors, cors, *same-origin
            redirect: "follow", // manual, *follow, error
            referrer: "no-referrer" // *client, no-referrer
        }).then(response => response.json()); // 輸出成 json
      }

      const store = new Vuex.Store({
        state: {
          list: [],
          dataSets:[
            paymentInfoDataSet:[]
          ]
        },
        actions:{
          getPaymentDetail({ commit }, { paymentId, url }) {
              postData(url, { paymentId: paymentId })
                  .then(response => {
                      commit("updatePaymentInfo", {
                          paymentId: paymentId,
                          info: response
                      });
                  })
          },
        },
        mutations: {
          [ENUMS.List.Number](state, list) {
            state.list = [...list];
          },
          [ENUMS.List.Country](state, list) {
            state.list = [...list];
          },
          ["updatePaymentInfo"](state, detail) {
              let newDataSet = [...dataSets.paymentInfoDataSet];
              let currentData = state.dataSets.paymentInfoDataSet.find(x => x.paymentId === detail.paymentId);
              if (currentData) {
                  let removeIndex = newDataSet.findIndex(function(item) {
                      return item.paymentId === detail.paymentId;
                  });
                  newDataSet.splice(removeIndex, 1);
              }
              newDataSet.push(detail);
              state.dataSets.paymentInfoDataSet = newDataSet;
          },
        },
        getters: {
          currentList(state) {
            return state.list;
          },
          currentListCount(state, getters) {
            return getters.currentList.length;
          }
        }
      });

      new Vue({
        el: "#app",
        store,
        data: {
          defList: ["american", "chinese", "japaneses", "europe", "africa"],
          numberList: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
        },
        computed: {
          list() {
            return this.$store.getters.currentList;
          },
          listCount() {
            return this.$store.getters.currentListCount;
          }
        },
        methods: {
          getCountryList() {
            store.commit(ENUMS.List.Country, this.defList);
          },
          getNumberList() {
            store.commit(ENUMS.List.Number, this.numberList);
          }
        }
      });
    </script>
  </body>
</html>
  1. getter 大概就相當於是 store 的計算屬性,結果會被 cache 起來,但如果使用方法的格式,有參數傳入,則仍舊是每次呼叫都會執行一次
  2. mutation 做資料更新的話,應該利用新對象替換掉舊對象。例如:state.obj = { ...state.obj, newProp: 123 }
  3. 使用常量替代 Mutation 事件類型,這個可以用也可以不用,但是大專案的話最好還是好好管理一下
  4. mutation 必須是同步函数,非同步的應該使用actions

非同步操作 Actions

Actions 透過 store.dispatch觸發:store.dispatch('getPaymentDetail') 在送出請求並獲得結果後,再呼叫 mutation 更新資料

// actions支持非同步操作
actions: {
  checkout ({ commit, state }, products) {
    // 把当前购物车的物品备份起来
    const savedCartItems = [...state.cart.added]
    // 发出结账请求,然后乐观地清空购物车
    commit(types.CHECKOUT_REQUEST)
    // 购物 API 接受一个成功回调和一个失败回调
    shop.buyProducts(
      products,
      // 成功操作
      () => commit(types.CHECKOUT_SUCCESS),
      // 失败操作
      () => commit(types.CHECKOUT_FAILURE, savedCartItems)
    )
  }
}

表單處理

若使用狀態管理,在表單處理上若使用 v-model,在嚴格模式下由於資料異動並非是由 mutation 處理,因此會拋出例外 比較簡便的做法是利用 computed 的 get、set,在 set 事件中來處理資料更新

<input v-model="message" />
computed: {
  message: {
    get () {
      return this.$store.state.obj.message
    },
    set (value) {
      this.$store.commit('updateMessage', value)
    }
  }
}
vuex