個人筆記-vuex

子非魚_ql發表於2019-03-29

個人筆記-vuex

最近想要沉澱下自己的知識體系,以前光看不記,當時記得,過段時間記憶就模糊了,好腦子不如爛筆頭,古人誠不欺我,所以現在決定給用自己的語言方式來給自己記個筆記。

vuex

vuex 有什麼好講的呢,現在的元件通訊有太多方法了,但這種作為vue全家桶裡的一員,使用還是蠻廣泛的,關於這方面的資料也很多,但我這個筆記是以自己的理解方式用大白話來講的,有不對的還講指正。(文章裡的程式碼摘自vuex的中文官網vuex.vuejs.org/zh/)

為什麼要用vuex

我們知道,vue是基於元件化的,各管各的,如果這個時候,兄弟元件之間的需要共用某個資料,該怎麼通訊呢,方法有很多,比如父子級別的可以通過props和$emit來解決,爺孫級別的可以通過$attrs和$listeners來解決,如果各自相互獨立的元件還可能通過中央匯流排事件來通訊,基本上這三種算是常見的了,當然,也有人說,可以做個全域性的變數,然後大家都去拿,在大型專案開發中,這個變數你存了某個值,然後又被另一個人改了,你根本不知道是找哪個人哪行程式碼改了,這個時候就需要我們進行一個約定,一個特定的地方,儲存我們共同的東西,通過約定的方法去更改,以便我們能定位到是誰改了,傳了什麼東西進來,又方便我們進行追蹤。vuex還有一個好處,他裡面存的是當前的狀態,這就意味著,這裡的發生了變化,能響應到對應的元件呼叫處。

共同的地方-Store

“store”基本上就是一個容器,它包含著你的應用中大部分的狀態 (state)。

Vue.use(Vuex) // 重點
const store = new Vuex.Store({
  state: {
    count: 0
  }
})
複製程式碼

store例項裡一定包含一個state物件,裡就會存放所有的共有資料狀態。

那麼元件裡面我要去取到這個狀態該怎麼做呢,首先要注入

const app = new Vue({
  el: '#app',
  // 把 store 物件提供給 “store” 選項,這可以把 store 的例項注入所有的子元件
  store,
  components: { Counter },
  template: `
    <div class="app">
      <counter></counter>
    </div>
  `
})
複製程式碼

在根元件裡注入,這樣所屬元件就能通過this.$store.state訪問到store例項裡的state物件了,其實這種方法在vue裡很常見,包括路由啊之類的,都是通過在根元件注入,所屬元件呼叫api的方式來取值的。

Getter

Getter有什麼用呢?之前我們不是可以直接通過this.$store.state訪問到例項裡的state物件了嗎,那還要這個有什麼用呢,在我看來,Getter有三個作用

  • 可以對this.$store.state裡的某個屬性做計算,比如排序啊,過濾之類的
  • 可以直接通過方法訪問,對state裡的資料進行篩選,比如store.getters.fun(id)
  • 進行對映,這就要用到mapGetters這個輔助函式:

mapGetters

import { mapGetters } from 'vuex'

export default {
  // ...
  computed: {
  // 使用物件展開運算子將 getter 混入 computed 物件中
    ...mapGetters([
      'doneTodosCount',
      'anotherGetter',
      // ...
    ])
  }
}
複製程式碼

這樣寫有什麼好處呢,這其實是一個對映,可能不同的理解解釋的不一樣,大家也不用糾結,這樣寫了之後可以通過this.doneTodosCount直接拿到,相當於把this.$store.state.doneTodosCount對映成了this.doneTodosCount,這樣大家就再也不用噼裡啪啦寫一堆重複的東西了。

Mutation

之前我們提到,我們要約定一個方法去更新state裡資料的狀態,這個約定的方法就是提交 mutation。

const store = new Vuex.Store({
  ...
  mutations: {
    increment (state) {
      // 變更狀態
      state.count++
    }
  }
})
複製程式碼

每一個mutation都類似一個事件,increment就是這個事件的type,你不能直接呼叫這個事件的回撥,需要以相應的 type 呼叫 store.commit 方法:store.commit('increment')

有時候我們要需要對傳入的資料進加以第三方資料進行運算,文件裡叫提交載荷

// ...
mutations: {
  increment (state, n) {
    state.count += n
  }
}
複製程式碼

呼叫store.commit('increment', 10)

文件裡說載荷應該是一個物件,在大多數情況下,載荷應該是一個物件,這樣可以包含多個欄位並且記錄的 mutation 會更易讀,這塊表示也蒙。。。

// ...
mutations: {
  increment (state, payload) {
    state.count += payload.amount
  }
}
複製程式碼

還有一種物件提交模式,我覺得沒啥用,跳過。

mapMutations

同mapGetter,也是對映成this.的寫法

import { mapMutations } from 'vuex'

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

      // `mapMutations` 也支援載荷:
      'incrementBy' // 將 `this.incrementBy(amount)` 對映為 `this.$store.commit('incrementBy', amount)`
    ]),
    ...mapMutations({
      add: 'increment' // 將 `this.add()` 對映為 `this.$store.commit('increment')`
    })
  }
}
複製程式碼

使用常量替代 Mutation 事件型別

這個看專案吧,大型專案可能需要,各種規範。

敲個黑板: mutation 都是同步事務,什麼是同步事務呢,簡單的來說就是狀態變更都應該在此刻完成,不能有非同步的操作,需要加入非同步的操作見後面的Action知識點。

Action

Action 類似於 mutation,不同在於:Action 提交的是 mutation,而不是直接變更狀態。Action 可以包含任意非同步操作。

const store = new Vuex.Store({
  ...
  mutations: {
    increment (state) {
      state.count++
    }
  },
  actions: {
    /* es6解構寫法
    increment ({ commit }) {
        commit('increment')
    }*/
    increment (context) {
      context.commit('increment')
    }
  }
})
複製程式碼

這個context是個什麼東西呢,文件上面是這麼說的:與 store 例項具有相同方法和屬性的 context 物件,因此你可以呼叫 context.commit 提交一個 mutation,或者通過 context.state 和 context.getters 來獲取 state 和 getters。

Action 通過 store.dispatch 方法觸發:store.dispatch('increment'),同時Action裡支援非同步操作,舉個簡單的例子

actions: {
  incrementAsync ({ commit }) {
    setTimeout(() => {
      commit('increment')
    }, 1000)
  }
}
複製程式碼

action裡的寫法基本上和mutationu差不多

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

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

mapActions

功能同之前的類似

組合 Action

組合Action,我個人用的不多,但原理要了解一下,Action支援非同步,所以在Action裡可以寫非同步的commit,包括Promise,包括多個commit依賴性的先後觸發。

Module

大型專案多人會用到的東西,各自管各自的模組。每個模組都有自己對應的一套完整的store,然後再通過modules組合到頂層的store裡去。

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 的狀態
複製程式碼

對於module,我覺得只需關注幾個點就可了:

模組內部的 mutation 和 getter,接收的第一個引數是模組的區域性狀態物件,而不是頂層的了。

const moduleA = {
  state: { count: 0 },
  mutations: {
    increment (state) {
      // 這裡的 `state` 物件是模組的區域性狀態
      state.count++
    }
  },
  actions: {
    incrementIfOddOnRootSum ({ state, commit, rootState }) {
      if ((state.count + rootState.count) % 2 === 1) {
        commit('increment')
      }
    }
  },
  getters: {
    sumWithRootCount (state, getters, rootState) {
      return state.count + rootState.count
    }
  }
}
複製程式碼

關於這塊,我覺得只需關注幾個點就Ok了。

現在的state都指區域性的狀態物件了,頂級的狀態為rootState

const moduleA = {
  // ...
  getters: {
    sumWithRootCount (state, getters, rootState) {
      return state.count + rootState.count
    }
  },
  actions: {
    incrementIfOddOnRootSum ({ state, commit, rootState }) {
      if ((state.count + rootState.count) % 2 === 1) {
        commit('increment')
      }
    }
  }
}
複製程式碼

引數和非module一樣,只不過多了第三個引數,這個引數指向頂級狀態物件。

名稱空間

預設情況下,模組內部的action ,mutation,getter是註冊在全域性名稱空間的–這樣使得多個模組能夠對同一mutation或者action做出響應;如果希望你的模組更加自包含或者提高可重用性,你可以通過新增namespaced:true 的方式使其成為名稱空間模組,當模組被註冊後,他的所有getter,action,mutation都會自動根據模組註冊的路徑調整命名;x

相關文章