Vuex基本使用的總結

weixin_33797791發表於2018-01-12

使用

在 Vue 的單頁面應用中使用,需要使用Vue.use(Vuex)呼叫外掛。
使用非常簡單,只需要將其注入到Vue根例項中。

import Vuex from 'vuex'
Vue.use(Vuex)
const store = new Vuex.Store({
  state: {
    count: 0
  },
getter: {
    doneTodos: (state, getters) => {
      return state.todos.filter(todo => todo.done)
    }
  },
  mutations: {
    increment (state, payload) {
      state.count++
    }
  },
actions: {
  addCount(context) {
    // 可以包含非同步操作
    // context 是一個與 store 例項具有相同方法和屬性的 context 物件
  }
}
})
// 注入到根例項
new Vue({
  el: '#app',
  store,
  template: '<App/>',
  components: { App }
})

然後改變狀態:

this.$store.commit('increment')

Vuex 主要有四部分:

  1. state:包含了store中儲存的各個狀態。
  2. getter: 類似於 Vue 中的計算屬性,根據其他 getter 或 state 計算返回值。
  3. mutation: 一組方法,是改變store中狀態的執行者。
  4. action: 一組方法,其中可以含有非同步操作。

state

Vuex 使用 state來儲存應用中需要共享的狀態。為了能讓 Vue 元件在 state更改後也隨著更改,需要基於state建立計算屬性。

const Counter = {
  template: `<div>{{ count }}</div>`,
  computed: {
    count () {
      return this.$store.state.count  // count 為某個狀態
    }
  }
}

getters

類似於 Vue 中的 計算屬性,可以在所以來的其他 state或者 getter改變後自動改變。
每個getter方法接受 state和其他getters作為前兩個引數。

getters: {
    doneTodos: (state, getters) => {
      return state.todos.filter(todo => todo.done)
    }
  }

mutations

前面兩個都是狀態值本身,mutations才是改變狀態的執行者。mutations用於同步地更改狀態

// ...
mutations: {
  increment (state, n) {
    state.count += n
  }
}

其中,第一個引數是state,後面的其他引數是發起mutation時傳入的引數。

this.$store.commit('increment', 10)

commit方法的第一個引數是要發起的mutation名稱,後面的引數均當做額外資料傳入mutation定義的方法中。
規範的發起mutation的方式如下:

store.commit({
  type: 'increment',
  amount: 10   //這是額外的引數
})

額外的引數會封裝進一個物件,作為第二個引數傳入mutation定義的方法中。

mutations: {
  increment (state, payload) {
    state.count += payload.amount
  }
}

actions

想要非同步地更改狀態,需要使用actionaction並不直接改變state,而是發起mutation

actions: {
  incrementAsync ({ commit }) {
    setTimeout(() => {
      commit('increment')
    }, 1000)
  }
}

發起action的方法形式和發起mutation一樣,只是換了個名字dispatch

// 以物件形式分發
store.dispatch({
  type: 'incrementAsync',
  amount: 10
})

action處理非同步的正確使用方式

想要使用action處理非同步工作很簡單,只需要將非同步操作放到action中執行(如上面程式碼中的setTimeout)。
要想在非同步操作完成後繼續進行相應的流程操作,有兩種方式:

  1. action返回一個 promise
    dispatch方法的本質也就是返回相應的action的執行結果。所以dispatch也返回一個promise
store.dispatch('actionA').then(() => {
// ...
})
  1. 利用async/await。程式碼更加簡潔。
// 假設 getData() 和 getOtherData() 返回的是 Promise

actions: {
  async actionA ({ commit }) {
    commit('gotData', await getData())
  },
  async actionB ({ dispatch, commit }) {
    await dispatch('actionA') // 等待 actionA 完成
    commit('gotOtherData', await getOtherData())
  }
}

各個功能與 Vue 元件結合

stategetter結合進元件需要使用計算屬性

computed: {
    count () {
      return this.$store.state.count 
      // 或者 return this.$store.getter.count2
    }
  }

mutationaction結合進元件需要在methods中呼叫this.$store.commit()或者this.$store.commit():

methods: {
    changeDate () {
        this.$store.commit('change');
    },
    changeDateAsync () {
        this.$store.commit('changeAsync');
    }
}

為了簡便起見,Vuex 提供了四個方法用來方便的將這些功能結合進元件。

  1. mapState
  2. mapGetters
  3. mapMutations
  4. mapActions

示例程式碼:

import { mapState, mapGetters, mapMutations, mapActions } from 'vuex'

// ....
computed: {
  localComputed () { /* ... */ },
  ...mapState({
    // 為了能夠使用 `this` 獲取區域性狀態,必須使用常規函式
    count(state) {
      return state.count + this.localCount
    }
  }),
  ...mapGetters({
    getterCount(state, getters) {
      return state.count + this.localCount
    }
  })
}
methods: {
  ...mapMutations({
       add: 'increment' // 將 `this.add()` 對映為`this.$store.commit('increment')`
    }),
  ...mapActions({
      add: 'increment' // 將 `this.add()` 對映為 `this.$store.dispatch('increment')`
    })
}

如果結合進元件之後不想改變名字,可以直接使用陣列的方式。

methods: {
    ...mapActions([
      'increment', // 將 `this.increment()` 對映為 `this.$store.dispatch('increment')`

      // `mapActions` 也支援載荷:
      'incrementBy' // 將 `this.incrementBy(amount)` 對映為 `this.$store.dispatch('incrementBy', amount)`
    ]),
}

store分割為模組。

可以將應用的store分割為小模組,每個模組也都擁有所有的東西:state, getters, mutations, actions
首先建立子模組的檔案:

// initial state
const state = {
  added: [],
  checkoutStatus: null
}
// getters
const getters = {
  checkoutStatus: state => state.checkoutStatus
}
// actions
const actions = {
  checkout ({ commit, state }, products) {
  }
}
// mutations
const mutations = {
  mutation1 (state, { id }) {
  }
}
export default {
  state,
  getters,
  actions,
  mutations
}

然後在總模組中引入:

import Vuex from 'vuex'
import products from './modules/products' //引入子模組

Vue.use(Vuex)
export default new Vuex.Store({
  modules: {
    products   // 新增進模組中
  }
})

其實還存在名稱空間的概念,大型應用會使用。需要時檢視文件即可。Vuex的基本使用大致如此。

相關文章