Vue 之 vuex

由于Vuex的官方文檔在各個(gè)模塊之間缺乏一些過(guò)渡塞关,另外新概念很多,使得初讀時(shí)總有些云里霧里的感覺(jué)。于是本文在官方文檔的基礎(chǔ)上補(bǔ)充了一些自己的理解及相關(guān)的背景知識(shí)璧眠,尤其是試圖讓各個(gè)部分之間形成一定的連貫關(guān)系足陨。希望能幫助初學(xué)者更快地理解Vuex嫂粟。

一、Vuex是干什么用的墨缘?

它是用于對(duì)復(fù)雜應(yīng)用進(jìn)行狀態(tài)管理用的(官方說(shuō)法是它是一種狀態(tài)管理模式)星虹。

“殺雞不用宰牛刀”。對(duì)于簡(jiǎn)單的項(xiàng)目镊讼,根本用不著Vuex這把“宰牛刀”宽涌。那簡(jiǎn)單的項(xiàng)目用什么呢?用Vue.js官方提供的事件總線就可以了蝶棋。

二护糖、我們import進(jìn)來(lái)的Vuex對(duì)象都包含些什么呢?

我們使用Vuex的時(shí)候怎么用呢嚼松?通常都是這樣:

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

Vue.use(Vuex);

new Vuex.Store({
    state: {     //放置state的值
        count: 0,
        str:"abcd234"
    },
    getters: {   //放置getters方法
        strLen: state => state.str.length
    },
    // mutations只能是同步操作
    mutations: {   //放置mutations方法
       increment(state, payload) {
          //在這里改變state中的數(shù)據(jù)
          state.count = payload.number;
       }
    },
    // actions可以是異步操作
    actions: {      //放置actions方法
        actionName({ commit }) {
            //dosomething
            commit('mutationName')
        },
        getSong ({commit}, id) {
            api.getMusicUrlResource(id).then(res => {
                let url = res.data.data[0].url;
            })
            .catch((error) => {  // 錯(cuò)誤處理
                console.log(error);
            });
        }
    }
});

new Vue({
  el: '#app',
  store,
  ...
});

這里import進(jìn)來(lái)的Vuex是個(gè)什么東西呢嫡良?我們用console.log把它輸出一下:

console.log(Vuex)

通過(guò)輸出,我們發(fā)現(xiàn)其結(jié)構(gòu)如下:

{

? Store: function Store(){},

? mapActions: function(){}, // 對(duì)應(yīng)Actions的結(jié)果集

? mapGetters: function(){}, // 對(duì)應(yīng)Getters的結(jié)果集

? mapMutations: function(){}, // 對(duì)應(yīng)Mutations的結(jié)果集

? mapState: function(){}, // 對(duì)應(yīng)State的結(jié)果集

? install: function install(){},

? installed: true

}

可見(jiàn)献酗,import進(jìn)來(lái)的Vuex它實(shí)際上是一個(gè)對(duì)象寝受,里面包含了Store這一構(gòu)造函數(shù),還有幾個(gè)mapActions罕偎、mapGetters很澄、mapMutations、mapState這幾個(gè)輔助方法(后面再講)。

除此之外甩苛,還有一個(gè)install方法蹂楣。我們發(fā)現(xiàn),import之后要對(duì)其進(jìn)行Vue.use(Vuex);的操作讯蒲。根據(jù)這兩個(gè)線索痊土,我們就明白了,Vuex本質(zhì)上就是一個(gè)Vue.js的插件墨林。不信你去對(duì)照下Vue.js的官方文檔中對(duì)于插件的解釋就知道了(見(jiàn)這里)赁酝。

三、創(chuàng)建好的store實(shí)例怎么在各個(gè)組件中都能引用到旭等?

new Vuex.Store實(shí)例酌呆,怎么才能在各個(gè)組件中都能引用到呢?因?yàn)檫@個(gè)store實(shí)例是個(gè)全局單例搔耕。Vuex 通過(guò) store 選項(xiàng)隙袁,提供了一種機(jī)制將狀態(tài)從根組件“注入”到每一個(gè)子組件中:

const app = new Vue({
  el: '#app',
  // 把 store 對(duì)象提供給 “store” 選項(xiàng),這可以把 store 的實(shí)例注入所有的子組件
  store,
  components: { Counter },
  template: `
    <div class="app">
      <counter></counter>
    </div>
  `
})

通過(guò)在根實(shí)例中注冊(cè) store 選項(xiàng)弃榨,該 store 實(shí)例會(huì)注入到根組件下的所有子組件中藤乙,且子組件能通過(guò) this.$store訪問(wèn)到。

四惭墓、Vuex中的幾大核心概念

1、State

這個(gè)很好理解而姐,就是狀態(tài)數(shù)據(jù)腊凶。Vuex所管理的就是狀態(tài),其它的如Actions拴念、Mutations都是來(lái)輔助實(shí)現(xiàn)對(duì)狀態(tài)的管理的钧萍。Vue組件要有所變化,也是直接受到State的驅(qū)動(dòng)來(lái)變化的政鼠。

可以通過(guò)this.$store.state來(lái)直接獲取狀態(tài)风瘦,也可以利用vuex提供的mapState輔助函數(shù)將state映射到計(jì)算屬性(computed)中去。

mapState 輔助函數(shù)

當(dāng)一個(gè)組件需要獲取多個(gè)狀態(tài)時(shí)候公般,將這些狀態(tài)都聲明為計(jì)算屬性會(huì)有些重復(fù)和冗余万搔。為了解決這個(gè)問(wèn)題,我們可以使用 mapState 輔助函數(shù)幫助我們生成計(jì)算屬性官帘,讓你少按幾次鍵:

// 在單獨(dú)構(gòu)建的版本中輔助函數(shù)為 Vuex.mapState
import { mapState } from 'vuex'

export default {
  // ...
  computed: mapState({
    // 箭頭函數(shù)可使代碼更簡(jiǎn)練
    count: state => state.count,

    // 傳字符串參數(shù) 'count' 等同于 `state => state.count`
    countAlias: 'count',

    // 為了能夠使用 `this` 獲取局部狀態(tài),必須使用常規(guī)函數(shù)
    countPlusLocalState (state) {
      return state.count + this.localCount
    }
  })
}

當(dāng)映射的計(jì)算屬性的名稱與 state 的子節(jié)點(diǎn)名稱相同時(shí),我們也可以給 mapState 傳一個(gè)字符串?dāng)?shù)組褥蚯。

computed: mapState([
  // 映射 this.count 為 store.state.count
  'count'
])

2拔创、Getters

Getters本質(zhì)上是用來(lái)對(duì)狀態(tài)進(jìn)行加工處理。Getters與State的關(guān)系,就像Vue.js的computed與data的關(guān)系胖缤。getter 的返回值會(huì)根據(jù)它的依賴被緩存起來(lái)尚镰,且只有當(dāng)它的依賴值發(fā)生了改變才會(huì)被重新計(jì)算。

可以通過(guò)this.$store.getters.valueName對(duì)派生出來(lái)的狀態(tài)進(jìn)行訪問(wèn)哪廓」钒Γ或者直接使用輔助函數(shù)mapGetters將其映射到本地計(jì)算屬性中去。

mapGetters 輔助函數(shù)

mapGetters 輔助函數(shù)僅僅是將 store 中的 getter 映射到局部計(jì)算屬性:

import { mapGetters } from 'vuex'

export default {
  // ...
  computed: {
  // 使用對(duì)象展開(kāi)運(yùn)算符將 getter 混入 computed 對(duì)象中
    ...mapGetters([
      'doneTodosCount',
      'anotherGetter',
      // ...
    ])
  }
}

如果你想將一個(gè) getter 屬性另取一個(gè)名字撩独,使用對(duì)象形式:

mapGetters({
  // 映射 `this.doneCount` 為 `store.getters.doneTodosCount`
  doneCount: 'doneTodosCount'
})

上面這段代碼中敞曹,有個(gè)寫法大家可能會(huì)奇怪,就是...mapGetters综膀。這是什么鬼東西呢澳迫?

其中mapGetters實(shí)際上是一個(gè)方法Vuex對(duì)象上的一個(gè)方法,這從本文開(kāi)頭打印的那個(gè)Vuex對(duì)象的內(nèi)容可以看出來(lái)剧劝¢系牵…這個(gè)符號(hào)是ES2015的一個(gè)新的語(yǔ)法糖,即將mapGetters處理后的內(nèi)容展開(kāi)后填入讥此。

3拢锹、Mutations

Mutations的中文意思是“變化”,利用它可以更改狀態(tài)萄喳。事實(shí)上卒稳,更改 Vuex 的 store 中的狀態(tài)的唯一方法就是提交 (commit)mutation。不過(guò)他巨,mutation觸發(fā)狀態(tài)改變的方式有一點(diǎn)特別充坑,所謂commit一個(gè)mutation,實(shí)際是像觸發(fā)一個(gè)事件一樣染突,傳入一個(gè)mutation的類型以及攜帶一些數(shù)據(jù)(稱作payload捻爷,載荷)。

本文開(kāi)頭那段代碼中所指明的mutations份企,即:

mutations: {   //放置mutations方法
    increment(state, payload) {
        //在這里改變state中的數(shù)據(jù)
        state.count = payload.number;
    }
},

其中所包含的也榄,實(shí)際上是一個(gè)個(gè)的mutation處理函數(shù),用于指明收到這個(gè)mutation的commit之后司志,應(yīng)該做些什么(當(dāng)然甜紫,主要就是改變state,只是改變哪些state值的問(wèn)題)骂远。

那commit一個(gè)mutation在代碼層面怎么表示呢棵介?

this.$store.commit('increment', {
  amount: 10
})

或者這樣:

this.$store.commit({
  type: 'increment',
  amount: 10
})

除了這種使用 this.$store.commit('xxx') 提交 mutation的方式之外,還有一種方式吧史,即使用 mapMutations 輔助函數(shù)將組件中的 methods 映射為 this.$store.commit邮辽。例如:

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')`
    })
  }
}

經(jīng)過(guò)這樣的映射之后唠雕,就可以通過(guò)調(diào)用方法的方式來(lái)觸發(fā)其對(duì)應(yīng)的(所映射到的)mutation commit了,比如吨述,上例中調(diào)用add()方法岩睁,就相當(dāng)于執(zhí)行了this.$store.commit('increment')了。

像上面這樣揣云,就既聲明了收到mutation后怎么處理捕儒,又清楚了怎么觸發(fā)一個(gè)mutation。是不是特別像事件的處理函數(shù)(handler)以及事件的觸發(fā)(emit)之間的關(guān)系邓夕?

考慮到觸發(fā)的mutation的type必須與mutations里聲明的mutation名稱一致刘莹,比較好的方式是把這些mutation都集中到一個(gè)文件(如mutation-types)中以常量的形式定義,在其它地方再將這個(gè)文件引入焚刚,便于管理点弯。而且這樣做還有一個(gè)好處,就是整個(gè)應(yīng)用中一共有哪些mutation type可以一目了然矿咕。就像下面這樣:

// mutation-types.js
export const SOME_MUTATION = 'SOME_MUTATION'

// store.js
import Vuex from 'vuex'
import { SOME_MUTATION } from './mutation-types'

const store = new Vuex.Store({
  state: { ... },
  mutations: {
    // 我們可以使用 ES2015 風(fēng)格的計(jì)算屬性命名功能來(lái)使用一個(gè)常量作為函數(shù)名
    [SOME_MUTATION] (state) {
      // mutate state
    }
  }
})

值得注意的是抢肛,mutation必須是同步函數(shù),目的是為了在dev-tools中能夠捕捉到每一條mutation前后狀態(tài)的快照(參見(jiàn)https://www.zhihu.com/question/48759748)碳柱。

4捡絮、Actions

與必須是同步函數(shù)Mutation所不同,Action可以包含異步的操作莲镣。

const store = new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    increment (state) {
      state.count++
    }
  },
  actions: {
    increment (context) {
      context.commit('increment')
    }
  }
})

或者用ES2015的參數(shù)解構(gòu)福稳,可以簡(jiǎn)寫成:

actions: {
    increment ({commit}) {
        commit('increment')
    }
}

和mutation類似,我們像上面這樣生命action的處理函數(shù)瑞侮。它接收的第一個(gè)參數(shù)是一個(gè)與 store 實(shí)例具有相同方法和屬性的 context 對(duì)象的圆,因此你可以調(diào)用 context.commit 提交一個(gè) mutation,或者通過(guò) context.statecontext.getters 來(lái)獲取 state 和 getters区岗。

不過(guò),mutation處理函數(shù)中所做的事情是改變state毁枯,而action處理函數(shù)中所做的事情則是commit mutation慈缔。

那么,怎么觸發(fā)action呢种玛?按照Vuex的叫法藐鹤,這叫分發(fā)(dispatch),我覺(jué)得這個(gè)名字讓人不好理解赂韵,我們反正知道它實(shí)際上是觸發(fā)的意思就行了娱节。具體的觸發(fā)方法是this.$store.dispatch(actionType, payload)。所傳的兩個(gè)參數(shù)一個(gè)是要觸發(fā)的action的類型祭示,一個(gè)是所攜帶的數(shù)據(jù)(payload)肄满,類似于上文所講的commit mutation時(shí)所傳的那兩個(gè)參數(shù)。具體如下:

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

或

// 以對(duì)象形式分發(fā)
this.$store.dispatch({
  type: 'incrementAsync',
  amount: 10
})

還有一種方法是使用 mapActions 輔助函數(shù)將組件的 methods 映射為 this.$store.dispatch 調(diào)用。如下:

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')`
    })
  }
}

另外稠歉,你需要知道掰担, this.$store.dispatch 可以處理被觸發(fā)的 action 的處理函數(shù)返回的 Promise,并且 this.$store.dispatch 仍舊返回 Promise怒炸。

再來(lái)看一些組合性的異步操作:

actions: {
  actionA ({ commit }) {
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        commit('someMutation')
        resolve()
      }, 1000)
    })
  }
}

現(xiàn)在你可以:

$this.store.dispatch('actionA').then(() => {
  // ...
})

在另外一個(gè) action 中也可以:

actions: {
  // ...
  actionB ({ dispatch, commit }) {
    return dispatch('actionA').then(() => {
      commit('someOtherMutation')
    })
  }
}

最后带饱,如果我們利用 async / await 這個(gè) JavaScript 即將到來(lái)的新特性,我們可以像這樣組合 action:

// 假設(shè) getData() 和 getOtherData() 返回的是 Promise

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

這實(shí)際上就實(shí)現(xiàn)了action嵌套另一個(gè)action的情況阅羹。

講了這么多action勺疼,你可能會(huì)納悶一個(gè)問(wèn)題:action這鬼東西不是和mutation看起來(lái)好多地方都很像嗎,直接觸發(fā)mutation去改變state豈不更好捏鱼?為什么還要先觸發(fā)action执庐,再由action去觸發(fā)mutation才達(dá)到改變state的目的?

這是個(gè)好問(wèn)題穷躁。還記得上面我們提到過(guò)mutation只能是同步的操作而action可以是包含異步操作嗎耕肩?那么,若想進(jìn)行異步操作问潭,通過(guò)mutation顯然是無(wú)法完成的猿诸,所以就有了action。我們來(lái)看一個(gè)更加實(shí)際的購(gòu)物車示例狡忙,涉及到調(diào)用異步 API分發(fā)多重 mutation

actions: {
  checkout ({ commit, state }, products) {
    // 把當(dāng)前購(gòu)物車的物品備份起來(lái)
    const savedCartItems = [...state.cart.added]
    // 發(fā)出結(jié)賬請(qǐng)求梳虽,然后樂(lè)觀地清空購(gòu)物車
    commit(types.CHECKOUT_REQUEST)
    // 購(gòu)物 API 接受一個(gè)成功回調(diào)和一個(gè)失敗回調(diào)
    shop.buyProducts(
      products,
      // 成功操作
      () => commit(types.CHECKOUT_SUCCESS),
      // 失敗操作
      () => commit(types.CHECKOUT_FAILURE, savedCartItems)
    )
  }
}

5、Module

Module是什么概念呢灾茁?它實(shí)際上是對(duì)于store的一種切割窜觉。由于Vuex使用的是單一狀態(tài)樹(shù),這樣整個(gè)應(yīng)用的所有狀態(tài)都會(huì)集中到一個(gè)比較大的對(duì)象上面北专,那么禀挫,當(dāng)應(yīng)用變得非常復(fù)雜時(shí),store 對(duì)象就很可能變得相當(dāng)臃腫拓颓!

為了解決以上問(wèn)題语婴,Vuex 允許我們將 store 分割成一個(gè)個(gè)的模塊(module)。每個(gè)模塊擁有自己的 state驶睦、mutation砰左、action、getter场航、甚至是嵌套子模塊——從上至下進(jìn)行同樣方式的分割缠导。就像這樣:

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 的狀態(tài)
store.state.b // -> moduleB 的狀態(tài)

模塊的局部狀態(tài)

對(duì)于每個(gè)模塊內(nèi)部的 mutation 和 getter,接收的第一個(gè)參數(shù)就是模塊的局部狀態(tài)對(duì)象溉痢。

const moduleA = {
  state: { count: 0 },
  mutations: {
    increment (state) {
      // 這里的 `state` 對(duì)象是模塊的局部狀態(tài)
      state.count++
    }
  },

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

同樣僻造,對(duì)于模塊內(nèi)部的 action憋他,局部狀態(tài)通過(guò) context.state 暴露出來(lái),根節(jié)點(diǎn)狀態(tài)則為 context.rootState

const moduleA = {
  // ...
  actions: {
    incrementIfOddOnRootSum ({ state, commit, rootState }) {
      if ((state.count + rootState.count) % 2 === 1) {
        commit('increment')
      }
    }
  }
}

對(duì)于模塊內(nèi)部的 getter嫡意,根節(jié)點(diǎn)狀態(tài)會(huì)作為第三個(gè)參數(shù)暴露出來(lái):

const moduleA = {
  // ...
  getters: {
    sumWithRootCount (state, getters, rootState) {
      return state.count + rootState.count
    }
  }
}

命名空間

默認(rèn)情況下举瑰,模塊內(nèi)部的 action、mutation 和 getter 是注冊(cè)在全局命名空間的——這樣使得多個(gè)模塊能夠?qū)ν?mutation 或 action 作出響應(yīng)蔬螟。

如果希望你的模塊具有更高的封裝度和復(fù)用性此迅,你可以通過(guò)添加 namespaced: true 的方式使其成為命名空間模塊。當(dāng)模塊被注冊(cè)后旧巾,它的所有 getter耸序、action 及 mutation 都會(huì)自動(dòng)根據(jù)模塊注冊(cè)的路徑調(diào)整命名。例如:

const store = new Vuex.Store({
  modules: {
    account: {
      namespaced: true,

      // 模塊內(nèi)容(module assets)
      state: { ... }, // 模塊內(nèi)的狀態(tài)已經(jīng)是嵌套的了鲁猩,使用 `namespaced` 屬性不會(huì)對(duì)其產(chǎn)生影響
      getters: {
        isAdmin () { ... } // -> getters['account/isAdmin']
      },
      actions: {
        login () { ... } // -> dispatch('account/login')
      },
      mutations: {
        login () { ... } // -> commit('account/login')
      },

      // 嵌套模塊
      modules: {
        // 繼承父模塊的命名空間
        myPage: {
          state: { ... },
          getters: {
            profile () { ... } // -> getters['account/profile']
          }
        },

        // 進(jìn)一步嵌套命名空間
        posts: {
          namespaced: true,

          state: { ... },
          getters: {
            popular () { ... } // -> getters['account/posts/popular']
          }
        }
      }
    }
  }
})

啟用了命名空間的 getter 和 action 會(huì)收到局部化的 getter坎怪,dispatchcommit

在命名空間模塊內(nèi)訪問(wèn)全局內(nèi)容(Global Assets)

如果你希望使用全局 state 和 getter廓握,rootStaterootGetter 會(huì)作為第三和第四參數(shù)傳入 getter搅窿,也會(huì)通過(guò) context 對(duì)象的屬性傳入 action。

若需要在全局命名空間內(nèi)分發(fā) action 或提交 mutation隙券,將 { root: true } 作為第三參數(shù)傳給 dispatchcommit即可男应。

modules: {
  foo: {
    namespaced: true,

    getters: {
      // 在這個(gè)模塊的 getter 中,`getters` 被局部化了
      // 你可以使用 getter 的第四個(gè)參數(shù)來(lái)調(diào)用 `rootGetters`
      someGetter (state, getters, rootState, rootGetters) {
        getters.someOtherGetter // -> 'foo/someOtherGetter'
        rootGetters.someOtherGetter // -> 'someOtherGetter'
      },
      someOtherGetter: state => { ... }
    },

    actions: {
      // 在這個(gè)模塊中娱仔, dispatch 和 commit 也被局部化了
      // 他們可以接受 `root` 屬性以訪問(wèn)根 dispatch 或 commit
      someAction ({ dispatch, commit, getters, rootGetters }) {
        getters.someGetter // -> 'foo/someGetter'
        rootGetters.someGetter // -> 'someGetter'

        dispatch('someOtherAction') // -> 'foo/someOtherAction'
        dispatch('someOtherAction', null, { root: true }) // -> 'someOtherAction'

        commit('someMutation') // -> 'foo/someMutation'
        commit('someMutation', null, { root: true }) // -> 'someMutation'
      },
      someOtherAction (ctx, payload) { ... }
    }
  }
}

帶命名空間的綁定函數(shù)

當(dāng)使用 mapState, mapGetters, mapActionsmapMutations 這些函數(shù)來(lái)綁定命名空間模塊時(shí)沐飘,寫起來(lái)可能比較繁瑣:

computed: {
  ...mapState({
    a: state => state.some.nested.module.a,
    b: state => state.some.nested.module.b
  })
},
methods: {
  ...mapActions([
    'some/nested/module/foo',
    'some/nested/module/bar'
  ])
}

對(duì)于這種情況,你可以將模塊的空間名稱字符串作為第一個(gè)參數(shù)傳遞給上述函數(shù)牲迫,這樣所有綁定都會(huì)自動(dòng)將該模塊作為上下文耐朴。于是上面的例子可以簡(jiǎn)化為:

computed: {
  ...mapState('some/nested/module', {
    a: state => state.a,
    b: state => state.b
  })
},
methods: {
  ...mapActions('some/nested/module', [
    'foo',
    'bar'
  ])
}

而且,你可以通過(guò)使用 createNamespacedHelpers 創(chuàng)建基于某個(gè)命名空間輔助函數(shù)盹憎。它返回一個(gè)對(duì)象筛峭,對(duì)象里有新的綁定在給定命名空間值上的組件綁定輔助函數(shù):

import { createNamespacedHelpers } from 'vuex'

const { mapState, mapActions } = createNamespacedHelpers('some/nested/module')

export default {
  computed: {
    // 在 `some/nested/module` 中查找
    ...mapState({
      a: state => state.a,
      b: state => state.b
    })
  },
  methods: {
    // 在 `some/nested/module` 中查找
    ...mapActions([
      'foo',
      'bar'
    ])
  }
}

給插件開(kāi)發(fā)者的注意事項(xiàng)

如果你開(kāi)發(fā)的插件(Plugin)提供了模塊并允許用戶將其添加到 Vuex store,可能需要考慮模塊的空間名稱問(wèn)題陪每。對(duì)于這種情況影晓,你可以通過(guò)插件的參數(shù)對(duì)象來(lái)允許用戶指定空間名稱:

// 通過(guò)插件的參數(shù)對(duì)象得到空間名稱
// 然后返回 Vuex 插件函數(shù)
export function createPlugin (options = {}) {
  return function (store) {
    // 把空間名字添加到插件模塊的類型(type)中去
    const namespace = options.namespace || ''
    store.dispatch(namespace + 'pluginAction')
  }
}

模塊動(dòng)態(tài)注冊(cè)

在 store 創(chuàng)建之后,你可以使用 store.registerModule 方法注冊(cè)模塊:

// 注冊(cè)模塊 `myModule`
store.registerModule('myModule', {
  // ...
})
// 注冊(cè)嵌套模塊 `nested/myModule`
store.registerModule(['nested', 'myModule'], {
  // ...
})

之后就可以通過(guò) store.state.myModulestore.state.nested.myModule 訪問(wèn)模塊的狀態(tài)奶稠。

模塊動(dòng)態(tài)注冊(cè)功能使得其他 Vue 插件可以通過(guò)在 store 中附加新模塊的方式來(lái)使用 Vuex 管理狀態(tài)俯艰。例如捡遍,vuex-router-sync 插件就是通過(guò)動(dòng)態(tài)注冊(cè)模塊將 vue-router 和 vuex 結(jié)合在一起锌订,實(shí)現(xiàn)應(yīng)用的路由狀態(tài)管理。

你也可以使用 store.unregisterModule(moduleName) 來(lái)動(dòng)態(tài)卸載模塊画株。注意辆飘,你不能使用此方法卸載靜態(tài)模塊(即創(chuàng)建 store 時(shí)聲明的模塊)啦辐。

模塊重用

有時(shí)我們可能需要?jiǎng)?chuàng)建一個(gè)模塊的多個(gè)實(shí)例,例如:

  • 創(chuàng)建多個(gè) store蜈项,他們公用同一個(gè)模塊
  • 在一個(gè) store 中多次注冊(cè)同一個(gè)模塊

如果我們使用一個(gè)純對(duì)象來(lái)聲明模塊的狀態(tài)芹关,那么這個(gè)狀態(tài)對(duì)象會(huì)通過(guò)引用被共享,導(dǎo)致?tīng)顟B(tài)對(duì)象被修改時(shí) store 或模塊間數(shù)據(jù)互相污染的問(wèn)題紧卒。

實(shí)際上這和 Vue 組件內(nèi)的 data 是同樣的問(wèn)題侥衬。因此解決辦法也是相同的——使用一個(gè)函數(shù)來(lái)聲明模塊狀態(tài)(僅 2.3.0+ 支持):

const MyReusableModule = {
  state () {
    return {
      foo: 'bar'
    }
  },
  // mutation, action 和 getter 等等...
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市跑芳,隨后出現(xiàn)的幾起案子轴总,更是在濱河造成了極大的恐慌,老刑警劉巖博个,帶你破解...
    沈念sama閱讀 217,185評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件怀樟,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡盆佣,警方通過(guò)查閱死者的電腦和手機(jī)往堡,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)共耍,“玉大人虑灰,你說(shuō)我怎么就攤上這事≌骺埃” “怎么了瘩缆?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,524評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)佃蚜。 經(jīng)常有香客問(wèn)我庸娱,道長(zhǎng),這世上最難降的妖魔是什么谐算? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,339評(píng)論 1 293
  • 正文 為了忘掉前任熟尉,我火速辦了婚禮,結(jié)果婚禮上洲脂,老公的妹妹穿的比我還像新娘斤儿。我一直安慰自己,他們只是感情好恐锦,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,387評(píng)論 6 391
  • 文/花漫 我一把揭開(kāi)白布往果。 她就那樣靜靜地躺著,像睡著了一般一铅。 火紅的嫁衣襯著肌膚如雪陕贮。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,287評(píng)論 1 301
  • 那天潘飘,我揣著相機(jī)與錄音肮之,去河邊找鬼掉缺。 笑死,一個(gè)胖子當(dāng)著我的面吹牛戈擒,可吹牛的內(nèi)容都是我干的眶明。 我是一名探鬼主播,決...
    沈念sama閱讀 40,130評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼筐高,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼搜囱!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起柑土,我...
    開(kāi)封第一講書(shū)人閱讀 38,985評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤犬辰,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后冰单,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體幌缝,經(jīng)...
    沈念sama閱讀 45,420評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,617評(píng)論 3 334
  • 正文 我和宋清朗相戀三年诫欠,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了涵卵。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,779評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡荒叼,死狀恐怖轿偎,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情被廓,我是刑警寧澤坏晦,帶...
    沈念sama閱讀 35,477評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站嫁乘,受9級(jí)特大地震影響昆婿,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜蜓斧,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,088評(píng)論 3 328
  • 文/蒙蒙 一仓蛆、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧挎春,春花似錦看疙、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,716評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至脚线,卻和暖如春搁胆,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,857評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工丰涉, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人斯碌。 一個(gè)月前我還...
    沈念sama閱讀 47,876評(píng)論 2 370
  • 正文 我出身青樓一死,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親傻唾。 傳聞我的和親對(duì)象是個(gè)殘疾皇子投慈,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,700評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • ### store 1. Vue 組件中獲得 Vuex 狀態(tài) ```js //方式一 全局引入單例類 // 創(chuàng)建一...
    蕓豆_6a86閱讀 730評(píng)論 0 3
  • 安裝 npm npm install vuex --save 在一個(gè)模塊化的打包系統(tǒng)中,您必須顯式地通過(guò)Vue.u...
    蕭玄辭閱讀 2,934評(píng)論 0 7
  • 在 vue 開(kāi)發(fā)中冠骄,組件通信一直是一大痛點(diǎn)伪煤。 當(dāng)項(xiàng)目是很簡(jiǎn)單的 SPA 或者多入口項(xiàng)目時(shí),可以靠著 vue 自帶的...
    若年閱讀 1,690評(píng)論 0 4
  • State 單一狀態(tài)樹(shù) Vuex使用單一狀態(tài)樹(shù)——用一個(gè)對(duì)象就包含了全部的應(yīng)用層級(jí)狀態(tài)凛辣。至此它便作為一個(gè)“唯一數(shù)據(jù)...
    oWSQo閱讀 1,091評(píng)論 0 0
  • koa-ejs 文檔地址: https://www.npmjs.com/package/koa-ejs 1. 安裝...
    我的昵稱好聽(tīng)嗎閱讀 2,556評(píng)論 0 2