初步學習Vuex

Nomad_Lau發表於2019-05-03

什麼是vuex

vuex是專門為vue.js開發的狀態管理模式,它將所有元件的狀態集中的儲存在一起,並且用相應的規則使這些狀態以一種可預測的方式發生改變。

vuex是做什麼用的

前面說到vuex是專門為vue.js開發的狀態管理模式,那麼究竟什麼是“狀態管理模式”呢? 以我目前對狀態的理解就是元件在某個時刻呈現出的檢視、資料的狀態。在vue的專案開發中我們經常會遇到多個元件共享某個狀態。即:多個檢視依賴同一元件 或者 不同檢視的行為需要改變同一狀態。
對於這種情況父子元件之前通過props進行傳參是很繁瑣的,同時兄弟元件是沒辦法做到的。並且不同檢視之間只能通過事件觸發或直接引用的方式傳遞資料。這顯然是很麻煩的。
因此就有了vuex,他將元件的所有狀態抽離出來,儲存在全域性的一個倉庫裡,這樣不管元件在哪個位置都可以獲取到狀態或者觸發行為。vuex就像一個巨大的倉庫一樣可以為我們儲存著所有元件的狀態和改變狀態的行為,同時維持著檢視和狀態之間的獨立性,使得程式碼變得結構更加清晰且易維護。

vuex要如何使用

  • 首先安裝vuex

npm install vuex --save

  • 引入vuex

import Vuex from 'vuex'
vue.use(Vuex)

  • 建立一個 store,僅需要提供一個初始 state 物件和一些 mutation:
const store = new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    increment (state) {
      state.count++
    }
  }
})
複製程式碼
  • 獲取狀態物件
// 建立一個 Counter 元件
const Counter = {
  template: `<div>{{ count }}</div>`,
  computed: {
    count () {
      return store.state.count
    }
  }
}
複製程式碼

由於 Vuex 的狀態儲存是響應式的,從 store 例項中讀取狀態最簡單的方法就是在計算屬性中返回某個狀態

  • 通過 store.commit 方法觸發狀態變更:
store.commit('increment')
複製程式碼

注意:改變狀態的唯一方式就是通過commit提交mutation

  • mapState 輔助函式
    當元件獲取多個狀態時,為了避免因將狀態宣告為計算屬性而是程式碼重複冗餘,我們可以使用mapState輔助函式
//引入mapState
import { mapState } from 'vuex'

export default {
  // ...
  computed: mapState({
    // 箭頭函式可使程式碼更簡練
    count: state => state.count,

    // 傳字串引數 'count' 等同於 `state => state.count`
    countAlias: 'count',

    // 為了能夠使用 `this` 獲取區域性狀態,必須使用常規函式
    countPlusLocalState (state) {
      return state.count + this.localCount
    }
  })
}
複製程式碼

當對映的計算屬性的名稱與 state 的子節點名稱相同時,我們也可以給 mapState 傳一個字串陣列。

computed: mapState([
  // 對映 this.count 為 store.state.count
  'count'
])
複製程式碼
  • vuex中的Getter
    Getter相當於vuex中的計算屬性,是對一些狀態的操作(如果一些狀態的操作會被多次使用不妨將他們寫入Getter中呼叫),當他所依賴的狀態發生改變的時候,Getter返回的值也會發生相應的變化。
//Getter 接受 state 作為其第一個引數,接受其他 getter 作為第二個引數:
const store = new Vuex.Store({
  state: {
    todos: [
      { id: 1, text: '...', done: true },
      { id: 2, text: '...', done: false }
    ]
  },
  getters: {
    doneTodos: state => {
      return state.todos.filter(todo => todo.done)
    }
  }
})
複製程式碼

通過屬性訪問Getter

store.getters.doneTodos // -> [{ id: 1, text: '...', done: true }]
複製程式碼
  • mapGetters 輔助函式
    mapGetters 輔助函式僅僅是將 store 中的 getter 對映到區域性計算屬性:
import { mapGetters } from 'vuex'

export default {
  // ...
  computed: {
  // 使用物件展開運算子將 getter 混入 computed 物件中
    ...mapGetters([
      'doneTodosCount',
      'anotherGetter',
      // ...
    ])
  }
}
複製程式碼
  • Vuex 中的 mutation
    vuex中的mutation類似於事件,每個mutation都由代表此事件的名稱(type)和回撥函式兩部分組成,回撥函式中就是進行狀態更改的地方,在大型專案中為了使整個 app 包含的 mutation 一目瞭然,通常會使用常量替代 Mutation 事件型別,將這些常量單獨寫在一個.js檔案中。 Mutation 必須是同步函式,若要進行非同步操作可以將其寫在action中
//它會接受 state 作為第一個引數:
const store = new Vuex.Store({
  state: {
    count: 1
  },
  mutations: {
    increment (state) {
      // 變更狀態
      state.count++
    }
  }
})
複製程式碼
  • 提交載荷(Payload)
    你可以向 store.commit 傳入額外的引數,即 mutation 的 載荷(payload)大多數時候載荷是一個物件,這樣可以包含多個欄位並且記錄的 mutation 會更易讀:

兩種提交方式

//方式一
store.commit('increment', {
  amount: 10
})
*******
mutations: {
  increment (state, payload) {
    state.count += payload.amount
  }
}
//方式二  ——物件風格的提交方式
mutations: {
  increment (state, payload) {
    state.count += payload.amount
  }
}
*******
store.commit({
  type: 'increment',
  amount: 10
})
複製程式碼
  • vuex中的Action
    Action 類似於 mutation,不同在於:
  1. Action 提交的是 mutation,而不是直接變更狀態。
  2. Action 可以包含任意非同步操作。

註冊一個簡單的action

const store = new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    increment (state) {
      state.count++
    }
  },
  //action接受一個和store有相同屬性和方法的context 物件
  //因此你可以呼叫 context.commit 提交一個 mutation
  //通過 context.state 和 context.getters 來獲取 state 和 getters
  actions: {
    increment (context) {
      context.commit('increment')
    }
  }
})
複製程式碼
  • 觸發Action
store.dispatch('increment')

// 以載荷形式分發
store.dispatch('incrementAsync', {
  amount: 10
})

// 以物件形式分發
store.dispatch({
  type: 'incrementAsync',
  amount: 10
})
複製程式碼
  • 在元件中分發 Action
import { mapActions } from 'vuex'

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

      // `mapActions` 也支援載荷:
      'incrementBy' // 將 `this.incrementBy(amount)` 對映為 `this.$store.dispatch('incrementBy', amount)`
    ]),
    ...mapActions({
      add: 'increment' // 將 `this.add()` 對映為 `this.$store.dispatch('increment')`
    })
  }
}
複製程式碼
  • 組合 Action
  • store.dispatch可以處理action返回的promise,並且 store.dispatch 仍舊返回 Promise:
store.dispatch('actionA').then(() => {
  // 處理Action返回的promise
})
複製程式碼
  • 使用async / await時,組合Action
// 假設 getData() 和 getOtherData() 返回的是 Promise

actions: {
  async actionA ({ commit }) {
    commit('gotData', await getData())
  },
  async actionB ({ dispatch, commit }) {
    await dispatch('actionA') // 等待 actionA 完成
    commit('gotOtherData', await getOtherData())
  }
}
複製程式碼
  • vuex中的module
    如果將每個組建的狀態,都放在同一個store中,那麼整個store會變得很臃腫,所以vuex提供了module這個屬性,使得每個模組擁有自己的 state、mutation、action、getter、甚至是巢狀子模組
const moduleA = {
  state: { count: 0 },
  mutations: {
    increment (state) {
      // 這裡的 `state` 物件是模組的區域性狀態
      state.count++
    }
  },

  getters: {
    doubleCount (state) {
      return state.count * 2
    }
  }
}

const moduleB = {
  state: { ... },
  mutations: { ... },
  actions: { ... }
}

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

store.state.a // -> moduleA 的狀態
store.state.b // -> moduleB 的狀態
複製程式碼
  • 名稱空間
    如果希望你的模組具有更高的封裝度和複用性,你可以通過新增 namespaced: true 的方式使其成為帶名稱空間的模組。當模組被註冊後,它的所有 getter、action 及 mutation 都會自動根據模組註冊的路徑調整命名。
const store = new Vuex.Store({
  modules: {
    account: {
      namespaced: true,

      // 模組內容(module assets)
      state: { ... }, // 模組內的狀態已經是巢狀的了,使用 `namespaced` 屬性不會對其產生影響
      getters: {
        isAdmin () { ... } // -> getters['account/isAdmin']
      },
      actions: {
        login () { ... } // -> dispatch('account/login')
      },
      mutations: {
        login () { ... } // -> commit('account/login')
      },

      // 巢狀模組
      modules: {
        // 繼承父模組的名稱空間
        myPage: {
          state: { ... },
          getters: {
            profile () { ... } // -> getters['account/profile']
          }
        },

        // 進一步巢狀名稱空間
        posts: {
          namespaced: true,

          state: { ... },
          getters: {
            popular () { ... } // -> getters['account/posts/popular']
          }
        }
      }
    }
  }
})
複製程式碼

相關文章