Vuex入門教程

丨大麥發表於2018-07-01

前言

元件化應用構建是Vue的特點之一,因此我們在Vue的實際開發過程中會經常需要封裝自定義元件,以提高開發的效率。 而元件在大部分情況下並不會孤立的存在,它必然會與父元件和兄弟元件產生資料的互動。所以在這裡為大家總結兩種元件資料互動的方式:EventBus和利用Vuex框架進行狀態管理。

上一篇文章介紹瞭如果通過EventBus在Vue自定義元件中進行事件傳遞(Vue自定義元件事件傳遞:EventBus部分),而EventBus比較適合在中小型專案中使用;如果我們需要開發一套大型的專案,這時候我們應該考慮使用Vuex框架來統一進行資料狀態管理。

為了方便讀者理解Vuex的基本使用,我將繼續沿用上一篇文章的案例(顯然在單頁和中小型應用中,Vuex顯得不太必要),為大家介紹Vuex的使用。

為什麼有EventBus還要使用Vuex

為什麼我們有EventBus了,還要使用Vuex?

其實在Vuex官網就有很好的解釋:Vuex官網對Vuex是什麼的解釋

對於Vuex是什麼,我的理解是:

Vuex是資料狀態管理框架,主要做到了資料和檢視層的解耦

而已經有EventBus我們為什麼還要使用Vuex這個問題上,我的理解是:

我們仔細思考一下,當我們使用EventBus時,我們A控制元件給B控制元件和C控制元件傳遞資料後,B控制元件經過處理又要修改傳遞過來資料,繼續通知A控制元件和C控制元件修改資料,這樣在C檔案中就要多處監聽A和B的EventBus。這樣當有關於資料傳遞有bug時,我們就很難追溯到底是哪一個環節出了問題。

伴隨著我們業務的複雜程度變高,檢視的複雜度也會隨之升高,所以此時通過Vuex來對資料和檢視層解耦就顯得十分必要。

案例介紹(與上一篇"Vue自定義元件事件傳遞:EventBus部分"案例一致,可跳過)

本章節會有大量的程式碼示例,為了讓讀者閱讀輕鬆,做如下目錄和元件介紹。本章節主要運用了兩個子元件和一個父元件。

子元件檔名:SearchInput.vueSearchItem.vue

父元件檔名:StateView.vue

目錄結構展示:

Vuex入門教程

1、SearchInput.vue

元件介紹:一個輸入框,它會onInput方法去監聽輸入內容,並呼叫方法,將輸入框內的資料傳遞出去。

程式碼展示:

<template>
  <div>
    <input placeholder="搜尋內容"  v-model="searchContent"/>
  </div>
</template>

<script type="text/ecmascript-6">
  export default{
    data(){
      return{
        searchContent:""
      }
    },
    props:{

    }
  }
</script>

<style lang="stylus" rel="stylesheet/stylus">

</style>


複製程式碼

SearchItem.vue

元件介紹:一個span,它主要用來接收父元件傳遞的內容和接收同胞元件輸入框傳遞的內容,並進行展示。

程式碼示例:

<template>
    <span class="item">
      {{content}}
    </span>
</template>

<script type="text/ecmascript-6">
  export default{
    data(){
      return{
        content:this.itemContent
      }
    },
    props:{
      itemContent:{
        type:String,
        required:true
      }
    }
  }
</script>

<style lang="stylus" rel="stylesheet/stylus">
  .item
    background #f4f4f4
    padding 3px 5px
    box-sizing border-box
    display inline-block
    cursor pointer
</style>

複製程式碼

StateView.vue

元件介紹:父元件,主要展示頁面和載入子元件

程式碼示例:

<template>
  <div>
    <search-view></search-view>
    <div>
      <search-item :itemContent="content"/>
      <search-item itemContent="熱門搜尋2"/>
      <search-item itemContent="熱門搜尋3"/>
    </div>
    <div>{{content}}</div>

  </div>
</template>

<script type="text/ecmascript-6">
import searchView from '../components/SearchInput.vue'
import searchItem from '../components/SearchItem.vue'

export default{
  data () {
    return {
      content:"接收輸入框的值"
    }
  },
  components: {
    searchView,
    searchItem
  }
}

</script>

<style lang="stylus" rel="stylesheet/stylus">

</style>

複製程式碼

正文

全域性註冊Vuex

在開始介紹Vuex的內容前,我們需要在全域性中註冊一個Vuex物件。

檢視我們的目錄結構:

Vuex入門教程

1、建立Store檔案

我們建立了一個store資料夾,專門存放各種型別的store檔案。

在這我們建立了SearchStore.js檔案。它就是我們接下來主要的主角,關於Vuex的State、Getter、Mutation、Action都會圍繞它來展開。

SearchStore.js程式碼如下:

import Vue from 'vue'
import vuex from 'vuex'
Vue.use(vuex)
export default new vuex.Store({
})

複製程式碼

2、在main.js中注入SearchStore.js

main.js

import Vue from 'vue'
import App from './App'
import router from './router'
import vueResource from 'vue-resource'
import searchStore from './store/SearchStore'   //引入SearchStore.js
Vue.use(vueResource)
Vue.config.productionTip = false

/* eslint-disable no-new */
new Vue({
  el: '#app',
  router,
  store:searchStore,           //注入SearchStore
  components: { App },
  template: '<App/>'
})

複製程式碼

我們在main.js中引入了SearchStore.js檔案,並在Vue根元件中注入了SearchStore.js檔案,因此所有的子元件都能訪問到了這個SearchStore.js這個Store。

State

State就像一個全域性的資料倉,裡面存放著資料來源

這句話怎麼理解呢?

不急,我們看一個Demo例子,你就會對State一下就理解了。

我們修改SearchStore.js的檔案,給它加一個資料倉,即State:

import Vue from 'vue'
import vuex from 'vuex'
Vue.use(vuex)
export default new vuex.Store({
  state:{
    searchContent:"這是Store的searchContent內容"
  }
})

複製程式碼

然後我們在StateView.jsSearchInput.js的mounted()生命週期中去列印SearchStore.js中的searchContent:

StateView

<template>
  <div>
    <search-view></search-view>
    <div>
      <search-item :itemContent="content"/>
      <search-item itemContent="熱門搜尋2"/>
      <search-item itemContent="熱門搜尋3"/>
    </div>
    <div>{{content}}</div>

  </div>
</template>

<script type="text/ecmascript-6">
import searchView from '../components/SearchInput.vue'
import searchItem from '../components/SearchItem.vue'
import searchEvent from '../event/SearchEvent'
export default{
  data () {
    return {
      content:""
    }
  },
  mounted(){
    console.log("StateView======"+this.$store.state.searchContent)   //列印SearchStore中的searchContent
  },
  components: {
    searchView,
    searchItem
  }
}

</script>

<style lang="stylus" rel="stylesheet/stylus">

</style>

複製程式碼

SearchInput.js

<template>
  <div>
    <input placeholder="搜尋內容" @input="sendEvent" v-model="searchContent"/>
  </div>
</template>

<script type="text/ecmascript-6">
  import searchEvent from '../event/SearchEvent'
  export default{
    data(){
      return{
        searchContent:""
      }
    },
    mounted(){
      console.log("SearchInput======"+this.$store.state.searchContent)
    },
    props:{

    }
  }
</script>

<style lang="stylus" rel="stylesheet/stylus">

</style>

複製程式碼

控制檯輸出結果

Vuex入門教程

怎麼樣,對state有感覺了嘛?

State的感受結論:

1、state是一個資料儲存的倉庫,所有的資料來源都會存放在這裡,就類似元件中的data。

2、我們可以明顯感覺到,我們在SearchStore.js的state中的資料,可以在任意元件中通過this.$store.state訪問到,而不是像使用EventBus一樣,需要到處去呼叫$emit$on方法去監聽資料和拷貝資料副本,做到了資料和檢視層的解耦。

Getter

Getter 用來在獲取資料來源的時候,對資料來源進行一定的加工後再返回。

接下來我們在SearchStore.js中加入Getter,來感受下上面這句話所表達的意思。

SearchStore.js

import Vue from 'vue'
import vuex from 'vuex'
Vue.use(vuex)
export default new vuex.Store({
  state:{
    searchContent:"這是Store的searchContent內容"
  },
  getters:{     //在這裡加入getter
    getSearchContent(state){       
    //我們定義了一個getter函式,叫做gerSearchContent,它有一個引數,這個引數儲存了state物件。
    //即SearchStore.js的state物件
      return "加工後的searchContent===="+state.searchContent     //模擬加工資料,並將加工後的資料返回
    }
  }
})
複製程式碼

在這裡,我們在Store的getters中定義了一個getSearchContent的函式,這個函式接收一個引數,這個引數就是我們的state物件,在這個例子中,即:SearchStore.js的state物件。

然後我們模擬了資料加工的過程,返回一個資料加工後的資料。

接下來,讓我們在StateView的mounted()生命週期中,列印這個getter。

StateView.js

<template>
  <div>
    <search-view></search-view>
    <div>
      <search-item :itemContent="content"/>
      <search-item itemContent="熱門搜尋2"/>
      <search-item itemContent="熱門搜尋3"/>
    </div>
    <div>{{content}}</div>

  </div>
</template>

<script type="text/ecmascript-6">
import searchView from '../components/SearchInput.vue'
import searchItem from '../components/SearchItem.vue'
import searchEvent from '../event/SearchEvent'
export default{
  data () {
    return {
      content:""
    }
  },
  mounted(){
    console.log("StateView======"+this.$store.state.searchContent)   //列印SearchStore中的searchContent
    console.log("StateView======"+this.$store.getters.getSearchContent)  //列印SearchStore中的getSearchContent
  },
  components: {
    searchView,
    searchItem
  }
}

</script>

<style lang="stylus" rel="stylesheet/stylus">

</style>
複製程式碼

控制檯輸出的結果

Vuex入門教程

Getter的感受結論

1、getter主要用於在獲取資料時,對資料進行加工後返回。

2、與EventBus相比,通過Vuex的Getter,我們可以在Store中對資料做統一加工處理,利於日後的專案維護。

Mutation

對資料倉中的資料,進行修改。Mutation只支援同步方法

同樣,我們通過在SearchStore.js加入Mutation來感受Mutation的作用

SearchStore.js

import Vue from 'vue'
import vuex from 'vuex'
Vue.use(vuex)
export default new vuex.Store({
  state:{
    searchContent:"這是Store的searchContent內容"
  },
  getters:{
    getSearchContent(state){
      return "加工後的searchContent===="+state.searchContent
    }
  },
  mutations:{
    changeSearchContent(state,payload){
      /**
       * 傳遞單個資料引數
       */
      state.searchContent=payload     //修改state的searchContent資料
    },
    changeSearchContentByObject(state,payload){
      /**
       * 有時候我們需要再payload傳遞多個資料引數時,我們可以通過物件的方式傳遞
       */
      state.searchContent="通過Object物件來修改資料:"+payload.searchContent
    }
  }
})
複製程式碼

注意:上面我們定義了兩個Mutation方法,他們唯一的不同是一個接受的payload是單個引數;而在需要傳遞多個引數時,我們可以在payload中傳遞物件的方式,去傳遞多個引數。

接下來我們依舊修改StateView.js。

StateView.js

<template>
  <div>
    <search-view></search-view>
    <div>
      <search-item :itemContent="content"/>
      <search-item itemContent="熱門搜尋2"/>
      <search-item itemContent="熱門搜尋3"/>
    </div>
    <div>{{content}}</div>

  </div>
</template>

<script type="text/ecmascript-6">
import searchView from '../components/SearchInput.vue'
import searchItem from '../components/SearchItem.vue'
import searchEvent from '../event/SearchEvent'
export default{
  data () {
    return {
      content:""
    }
  },
  mounted(){
    console.log("StateView======"+this.$store.state.searchContent)   //列印SearchStore中的searchContent
    console.log("StateView======"+this.$store.getters.getSearchContent)  //列印SearchStore中的getSearchContent
    
    //通過this.$store.commit()方法修改state的searchContent引數
    //在payload中傳遞單個引數
    this.$store.commit("changeSearchContent","StateView中修改後的SearchContent")
    console.log("StateView======"+this.$store.state.searchContent)
    
    //當需要傳遞多個引數時,我們可以在payload中以物件的形式傳遞
    this.$store.commit("changeSearchContentByObject",{"searchContent":"StateView中修改後的SearchContent","testVal":"testVal"})
    console.log("StateView======"+this.$store.state.searchContent)
  },
  components: {
    searchView,
    searchItem
  }
}

</script>

<style lang="stylus" rel="stylesheet/stylus">

</style>
複製程式碼

StateVie.js中,我們通過this.store.commit()方法去呼叫mutations中的方法,它接收兩個引數:

1、第一個引數傳遞mutations中對應的方法名 2、第二個引數傳遞payload,即要傳遞的資料

控制檯輸出的結果

Vuex入門教程

Mutation的感受結論:

1、通過Mutation我們可以對資料倉中的資料進行修改,我們可以在組建中通過呼叫this.$store.commit()方法去呼叫對應的Mutation去修改資料。

2、Mutation中只能執行同步的方法,如果需要執行非同步方法,我們要使用接下來即將登場的Action。

Action

Action和Mutation類似,他們之處在於:1、Action 提交的是 mutation,而不是直接變更狀態。2、Action 可以包含任意非同步操作。

話不多說,我們直接修改SearchStore.js檔案:

SearchStore.js

import Vue from 'vue'
import vuex from 'vuex'
Vue.use(vuex)
export default new vuex.Store({
  state:{
    searchContent:"這是Store的searchContent內容"
  },
  getters:{
    getSearchContent(state){
      return "加工後的searchContent===="+state.searchContent
    }
  },
  mutations:{
    changeSearchContent(state,payload){
      /**
       * 傳遞單個資料引數
       */
      state.searchContent=payload     //修改state的searchContent資料
    },
    changeSearchContentByObject(state,payload){
      /**
       * 有時候我們需要再payload傳遞多個資料引數時,我們可以通過物件的方式傳遞
       */
      state.searchContent="通過Object物件來修改資料:"+payload.searchContent
    }
  },
  actions:{
    changeSearchContentByAction(context,payload){
      /**
       * 模擬非同步的過程,2000毫秒後通過commit()方法執行mumations中的changeSearchContentByObject方法改變資料
       * 同樣,payload可以是單資料或通過物件的方式,傳遞多個資料
       * 這裡只舉例通過物件傳遞多資料的情況
       */
      setTimeout(()=>{
        context.commit("changeSearchContentByObject",payload)
      },2000)
    }
  }
})
複製程式碼

在這裡我們模擬了非同步的過程,通過呼叫setTimeOut()函式,在2000毫秒後通過呼叫commit()函式來改變searchContent資料。

接下來我們來修改StateView.js來呼叫我們剛剛寫的changeSearchContentByAction

StateView.js

<template>
  <div>
    <search-view></search-view>
    <div>
      <search-item :itemContent="content"/>
      <search-item itemContent="熱門搜尋2"/>
      <search-item itemContent="熱門搜尋3"/>
    </div>
    <div>{{content}}</div>

  </div>
</template>

<script type="text/ecmascript-6">
import searchView from '../components/SearchInput.vue'
import searchItem from '../components/SearchItem.vue'
import searchEvent from '../event/SearchEvent'
export default{
  data () {
    return {
      content:""
    }
  },
  mounted(){
    console.log("StateView======"+this.$store.state.searchContent)   //列印SearchStore中的searchContent
    console.log("StateView======"+this.$store.getters.getSearchContent)  //列印SearchStore中的getSearchContent
    
    //通過this.$store.commit()方法修改state的searchContent引數
    //在payload中傳遞單個引數
    this.$store.commit("changeSearchContent","StateView中修改後的SearchContent")
    console.log("StateView======"+this.$store.state.searchContent)
    
    //當需要傳遞多個引數時,我們可以在payload中以物件的形式傳遞
    this.$store.commit("changeSearchContentByObject",{"searchContent":"StateView中修改後的SearchContent","testVal":"testVal"})
    console.log("StateView======"+this.$store.state.searchContent)
    
    //通過Action修改資料
    this.$store.dispatch("changeSearchContentByAction",{"searchContent":"action修改後的資料"})
    setTimeout(()=>{
      console.log("5000毫秒後StateView======"+this.$store.state.searchContent)
    },5000)
    
  },
  components: {
    searchView,
    searchItem
  }
}

</script>

<style lang="stylus" rel="stylesheet/stylus">

</style>
複製程式碼

我們在StateView.js中,通過this.$store.dispatch()函式去呼叫action,該函式接收兩個引數: 1、Store中Action對應的方法名 2、要傳遞的資料

** 控制檯輸出結果**

Vuex入門教程

Action的感受結論:

1、Action和Mutation類似,它只是能夠處理非同步的情況,最終通過commit()函式呼叫Mutation去修改資料。

2、通過this.$store.dispatch()去呼叫Action的方法。

Module

可以將Store模組化,然後通過Module整合在一起。

我們如果將所有的資料都寫入一個Store的state中,這時候我們獲取,修改資料。隨著專案不斷變大,我們這個Store中的state和Mutation、Getter、Action的數量和Store的程式碼行數就會爆炸性的增加,使得我們的Store變得維護困難。

這時候,我們希望把Store模組化,例如不同子元件的資料抽取出來寫成單獨的一個Store。

這時候我們就需要通過Module整合各個模組,然後在將Store掛在在根元件下。

現在我們不再以SearchStore舉例了,我們建立3個新的Store檔案

1、index.js Store,主要負責整合所有的Store模組。

2、SearchInputStore.js, 主要負責輸入框子元件SearchInput.js的資料維護

3、SearchItemStore.js,主要負責SearchItem.js子元件的資料維護

首先我們來看SearchInputStore.js和SearchItemStore.js:

SearchInputStore.js

export default {
  state: {
    searchContent:""
  },
  getters:{
    getSearchContent(state){
      return state.searchContent;
    }
  },
  mutations:{
    changeSearchContent(state,payload){
      state.searchContent=payload
    }
  }
}
複製程式碼

SearchItemStore.js

export default {
  state: {
    searchHistoryList:[]
  },
  getters:{
    getsearchHistoryList(state){
      return state.searchHistoryList;
    }
  },
  mutations:{
    changesearchHistoryList(state,payload){
      state.searchContent.push(payload)
    }
  }
}
複製程式碼

這裡,我們只是通過export default將SearchInputStore和SearchItemStore輸出。

接下來讓我們看index.js,它將SearchInputStore.jsSearchItemStore.js整合在一起

index.js

/**
 * Created by Vicky on 2018/6/22.
 */
import Vue from 'vue'
import vuex from 'vuex'
import searchInputStore from './SearchInputStore'
import searchItemStore from './SearchItemStore'
Vue.use(vuex)
export default new vuex.Store({
  modules:{
    searchInputStore:searchInputStore,
    searchItemStore:searchItemStore
  }
})

複製程式碼

在index中我們首先將SearchInputStore.jsSearchItemStore.js通過import引入,然後在Store的modules中將它們兩引入。

接下來我們將index.js掛載在根元件下,我們修改一下main.js:

main.js

import Vue from 'vue'
import App from './App'
import router from './router'
import vueResource from 'vue-resource'
import store from './store/index'   //引用index.js
// import searchStore from './store/SearchStore'
Vue.use(vueResource)
Vue.config.productionTip = false

/* eslint-disable no-new */
new Vue({
  el: '#app',
  router,
  store,    //修改部分,簡寫
  components: { App },
  template: '<App/>'
})
複製程式碼

這樣我們就成功整合了兩個Store模組,需要注意的是接下來我們訪問state物件的物件時,需要加上模組名

例如,我們要訪問SearchInputStore的searchContent的資料時,我們需要使用this.$store.state.searchInputStore.searchContent進行訪問。

namespaced: true

namespaced主要用於提高模組的封裝性和複用性

我們會遇到一個情況,我們不同模組的Store中可能會存在相同方法名的Mutation、Getter和Action,這時候我們呼叫commit()會呼叫所有相同方法名的Mutation;當我們Getter方法名重複時,會報錯;當我們呼叫dispatch()方法時,所有相同方法名的Action都會被執行。

這明顯是不符合常理的,它會對其他模組的資料造成汙染和異常。但在一個大型專案中,我們往往是多人協同開發的,所以我們很難避免大家在未告知的情況下,定義了相同名字的Mutation、Getter和Action。

此時,我們就需要namespaced來幫我們忙,以提高模組的封裝性和複用性。

接下來我們來修改index.jsSearchInputStore.js檔案:

index.js

import Vue from 'vue'
import vuex from 'vuex'
import searchInputStore from './SearchInputStore'
import searchItemStore from './SearchItemStore'
Vue.use(vuex)
export default new vuex.Store({
  state:{
    rootVal:"根節點Store的值"
  },
  getters:{
    getRootVal(state){
      return state.rootVal
    }
  },
  mutations:{
    changeRootVal(state,payload){
        console.log("根節點Store的changeRootVal被呼叫了")
        state.rootVal=payload
    }
  },
  actions:{
    changeRootValByAction(context,payload){
      console.log("根節點Store的changeRootValByAction被呼叫了")
      context.commit("changeRootVal",payload)
    }
  },
  modules:{
    searchInputStore:searchInputStore,
    searchItemStore:searchItemStore
  }
})


複製程式碼

我們給index.js加了State、Getter和Mutation,這是為了在後面演示子Store通過namespaced對資料模組進行封裝後,子Store如何去訪問根Store的Getter、如果通過commit()呼叫根Mutation和子Mutation;如何通過dispatch()呼叫根Action和子Action。

SearchInputStore.js

export default {
  namespaced:true,
  state: {
    searchContent:""
  },
  getters:{
    getSearchContent(state,getters,rootState,rootGetters){
      /**
       * state 代表了當前模組的state
       * getters 代表了當前模組的getters
       * rootState 代表了根Store的state,即index.js的Store,但也包含它的子Store,我們可以通過rootState.searchInputStore訪問searchItemStore;通過rootState.rootVal訪問根模組的State值。
       * rootGetters 代表了根Store的getters和它子Store的getter方法也會被掛載在rootGetters下,
       * 我們可以通過rootGetters["searchItemStore/getHistoryList"]去訪問searchItemStore模組下的getHistoryList
       * 通過rootGetters.getRootVal去訪問根模組的getRootVal
       */
      //通過rootState去訪問searchItemStore的State資料
      console.log(rootState.searchItemStore.searchHistoryList)
      //通過rootState去訪問index.js的State資料
      console.log(rootState.rootVal)

      //通過rootGetters去訪問searchItemStore的Getter
      console.log(rootGetters["searchItemStore/getHistoryList"])

      //通過rootGetters.getRootVal去訪問根模組的getRootVal
      console.log(rootGetters.getRootVal)

      return state.searchContent;
    }
  },
  mutations:{
    changeSearchContent(state,payload){
      /**
       * state 代表了當前模組的state
       * payload 代表了傳入的資料
       */
      //this.commit("searchItemStore/changeHistoryList",payload)呼叫SearchItemStore根模組的changeHistoryList
      this.commit("searchItemStore/changeHistoryList",payload)
      //通過this.commit("changeRootVal",payload)呼叫根模組的changeRootVal
      this.commit("changeRootVal",payload)
      console.log("changeSearchContent被呼叫了")
      state.searchContent=payload
    }
  },
  actions:{
    changeSearchContentByAction(context, payload){

      //呼叫本模組的changeSearchContent
      context.commit("changeSearchContent",payload)
      //呼叫本模組的otherAction
      context.dispatch("otherAction",payload)

      //呼叫根節點的Mutation和Action只需要傳入第三個引數物件,{root:true}即可
      context.commit("changeRootVal",payload,{root:true})
      //呼叫根節點的changeRootValByAction
      context.dispatch("changeRootValByAction",payload,{root:true})
    },
    otherAction(context,payload){
      console.log("otherAction被呼叫")
    }
  }
}

複製程式碼

SearchItemStore

export default {
  namespaced:true,
  state: {
    searchHistoryList:["歷史記錄1"]
  },
  getters:{
    getHistoryList(state){
      return state.searchHistoryList;
    }
  },
  mutations:{
    changeHistoryList(state,payload){
      state.searchHistoryList.push(payload)
    }
  },
  actions:{
    changeHistoryListByAction(context,payload){
      context.commit(changeHistoryList,payload)
    }
  }
}

複製程式碼

我們通過namespaced:true來對模組進行了封裝。

因為SearchInputStoreSearchItemStore被封裝了,我們子元件應該如何去訪問SearchInputStore呢?SearchInput又如何訪問其他被封裝模組和根Store的State、Getter、Mutation和Action呢?

子元件如何訪問封裝的模組(以訪問SearchInputStore為例)

訪問State

在訪問被封裝的State資料時,我們只需要做如下改變**(以訪問SearchInputStore的searchContent為例)**:

this.$store.state.searchContent改變成this.$store.state.searchInputStore.searchContent

訪問Getter

(以訪問SearchInputStore的getSearchContent為例):

this.$store.getters.getSearchContent改變成this.$store.getters["searchInputStore/getSearchContent"]

訪問Mutation

(以訪問SearchInputStore的changeSearchContent為例):

this.commit("changeSearchContent","新資料")改變成this.$store.commit("searchInputStore/changeSearchContent","新資料")

訪問Action

(以訪問SearchInputStore的changeSearchContentByAction為例):

this.dispatch("changeSearchContentByAction","新資料")改變成this.$store.dispatch("searchInputStore/changeSearchContentByAction","新資料")

封裝的模組如何訪問其他被封裝的模組和根模組(以訪問SearchItemStore為例)

先讓我們再仔細看下面的SearchInputStore的程式碼:

SearchInputStore

export default {
  namespaced:true,
  state: {
    searchContent:""
  },
  getters:{
    getSearchContent(state,getters,rootState,rootGetters){
      /**
       * state 代表了當前模組的state
       * getters 代表了當前模組的getters
       * rootState 代表了根Store的state,即index.js的Store,但也包含它的子Store,我們可以通過rootState.searchInputStore訪問searchItemStore;通過rootState.rootVal訪問根模組的State值。
       * rootGetters 代表了根Store的getters和它子Store的getter方法也會被掛載在rootGetters下,
       * 我們可以通過rootGetters["searchItemStore/getHistoryList"]去訪問searchItemStore模組下的getHistoryList
       * 通過rootGetters.getRootVal去訪問根模組的getRootVal
       */
      //通過rootState去訪問searchItemStore的State資料
      console.log(rootState.searchItemStore.searchHistoryList)
      //通過rootState去訪問index.js的State資料
      console.log(rootState.rootVal)

      //通過rootGetters去訪問searchItemStore的Getter
      console.log(rootGetters["searchItemStore/getHistoryList"])

      //通過rootGetters.getRootVal去訪問根模組的getRootVal
      console.log(rootGetters.getRootVal)

      return state.searchContent;
    }
  },
  mutations:{
    changeSearchContent(state,payload){
      /**
       * state 代表了當前模組的state
       * payload 代表了傳入的資料
       */
      //this.commit("searchItemStore/changeHistoryList",payload)呼叫SearchItemStore根模組的changeHistoryList
      this.commit("searchItemStore/changeHistoryList",payload)
      //通過this.commit("changeRootVal",payload)呼叫根模組的changeRootVal
      this.commit("changeRootVal",payload)
      console.log("changeSearchContent被呼叫了")
      state.searchContent=payload
    }
  },
  actions:{
    changeSearchContentByAction(context, payload){

      //呼叫本模組的changeSearchContent
      context.commit("changeSearchContent",payload)
      //呼叫本模組的otherAction
      context.dispatch("otherAction",payload)

      //呼叫根節點的Mutation和Action只需要傳入第三個引數物件,{root:true}即可
      context.commit("changeRootVal",payload,{root:true})
      //呼叫根節點的changeRootValByAction
      context.dispatch("changeRootValByAction",payload,{root:true})
    },
    otherAction(context,payload){
      console.log("otherAction被呼叫")
    }
  }
}
複製程式碼
訪問State和Getter

先看SearchInputStore的getters:

我們發現getSearchContent接受的引數從state變成了state,getters,rootState,rootGetters,它們的含義如下:

1、state 代表了當前模組的state

2、getters 代表了當前模組的getters

3、rootState 代表了根Store的state,即index.js的Store,但也包含它的子Store,我們可以通過rootState.searchInputStore訪問searchItemStore;通過rootState.rootVal訪問根模組的State值。

4、rootGetters 代表了根Store的getters和它子Store的getter方法也會被掛載在rootGetters下,我們可以通過rootGetters["searchItemStore/getHistoryList"]去訪問searchItemStore模組下的getHistoryList

訪問Mutation

(以訪問SearchInputStore的changeSearchContent為例):

在SearchInputStore的Mutation中可以通過:

1、this.commit("searchItemStore/changeHistoryList",payload)呼叫SearchItemStore根模組的changeHistoryList

2、this.commit("changeRootVal",payload)呼叫根模組的changeRootVal

訪問Action

(以訪問SearchInputStore的changeSearchContentByAction為例):

在SearchInputStore的Action中可以呼叫根模組的Mutation和Action,但不能呼叫其它封裝模組的Mutation,否則會報錯。

其實這也是有道理的,當我們本模組的Mutation非同步執行完畢,想要去修改其他模組的資料,只需要在自己模組的Mutation去呼叫其他模組的Mutation即可,沒有必要再去呼叫其他模組的Action。

因此我們可以在本模組中通過context.commit()cotext.dispatch()去呼叫本模組和根模組的Mutation和Action:

1、呼叫本模組的changeSearchContent:context.commit("changeSearchContent",payload)

2、 呼叫本模組的otherAction:context.dispatch("otherAction",payload)

3、呼叫根模組的Mutation和Action只需要傳入第三個引數物件,{root:true}即可:context.commit("changeRootVal",payload,{root:true})

4、呼叫根模組的changeRootValByAction:context.dispatch("changeRootValByAction",payload,{root:true})

最後覆盤

1、Vuex是資料和檢視層解耦的框架

2、在單頁和小專案中沒有必要使用Vuex。

3、Vuex中的State就像一個全域性的資料倉,裡面存放著資料來源。

4、Vuex中的Getter 用來在獲取資料來源的時候,對資料來源進行一定的加工後再返回。

5、Vuex中的Mutation用來對資料倉中的資料,進行修改。Mutation只支援同步方法。

6、Vuex中的Action和Mutation類似,他們之處在於:1、Action 提交的是 mutation,而不是直接變更狀態。2、Action 可以包含任意非同步操作。

7、Vuex中的Module可以將Store模組化,然後通過Module整合在一起。

8、關於...mapGetters、...mapMutation、...mapActions具體不展開,主要是通過es6的語法簡化程式碼書寫,可以參考官方文件: Vuex官方文件

9、看完本教程,由於內容較多,建議自己再看一次官方文件,並自己實現一個小Demo

我是大麥,如果喜歡我的文章,請給我一顆小心心。