從頭開始學習Vuex

1、前言

當咱們的應用遇到多個組件共享狀態時,會須要多個組件依賴於同一狀態抑或是來自不一樣視圖的行爲須要變動同一狀態。之前的解決辦法:html

a.將數據以及操做數據的行爲都定義在父組件;vue

b.將數據以及操做數據的行爲傳遞給須要的各個子組件(有可能須要多級傳遞)webpack

傳參的方法對於多層嵌套的組件將會很是繁瑣,而且對於兄弟組件間的狀態傳遞無能爲力。在搭建下面頁面時,你可能會對 vue 組件之間的通訊感到崩潰 ,特別是非父子組件之間通訊。此時就應該使用vuex,輕鬆能夠搞定組件間通訊問題。git

組件間通訊

2、什麼是Vuex

Vuex 是一個專爲 Vue.js 應用程序開發的狀態管理模式。它採用集中式存儲管理應用的全部組件的狀態,並以相應的規則保證狀態以一種可預測的方式發生變化。這裏的關鍵在於集中式存儲管理。簡單來講,對 vue 應用中多個組件的共享狀態進行集中式的管理(讀/寫)github

3、Vuex的原理是什麼

1.簡要介紹Vuex原理

image

Vuex實現了一個單向數據流,在全局擁有一個State存放數據,當組件要更改State中的數據時,必須經過Mutation進行,Mutation同時提供了訂閱者模式供外部插件調用獲取State數據的更新。而當全部異步操做(常見於調用後端接口異步獲取更新數據)或批量的同步操做須要走Action,但Action也是沒法直接修改State的,仍是須要經過Mutation來修改State的數據。最後,根據State的變化,渲染到視圖上。web

2.簡要介紹各模塊在流程中的主要功能:

  • Vue Components:Vue組件。HTML頁面上,負責接收用戶操做等交互行爲,執行dispatch方法觸發對應action進行迴應。
  • dispatch:操做行爲觸發方法,是惟一能執行action的方法。
  • actions:操做行爲處理模塊,由組件中的$store.dispatch('action 名稱', data1)來觸發。而後由commit()來觸發mutation的調用 , 間接更新 state。負責處理Vue Components接收到的全部交互行爲。包含同步/異步操做,支持多個同名方法,按照註冊的順序依次觸發。向後臺API請求的操做就在這個模塊中進行,包括觸發其餘action以及提交mutation的操做。該模塊提供了Promise的封裝,以支持action的鏈式觸發。
  • commit:狀態改變提交操做方法。對mutation進行提交,是惟一能執行mutation的方法。
  • mutations:狀態改變操做方法,由actions中的commit('mutation 名稱')來觸發。是Vuex修改state的惟一推薦方法。該方法只能進行同步操做,且方法名只能全局惟一。操做之中會有一些hook暴露出來,以進行state的監控等。
  • state:頁面狀態管理容器對象。集中存儲Vue components中data對象的零散數據,全局惟一,以進行統一的狀態管理。頁面顯示所需的數據從該對象中進行讀取,利用Vue的細粒度數據響應機制來進行高效的狀態更新。
  • getters:state對象讀取方法。圖中沒有單獨列出該模塊,應該被包含在了render中,Vue Components經過該方法讀取全局state對象。

4、何時使用Vuex

雖然 Vuex 能夠幫助咱們管理共享狀態,但也附帶了更多的概念和框架。這須要對短時間和長期效益進行權衡。
若是您的應用夠簡單,您最好不要使用 Vuex,由於使用 Vuex 多是繁瑣冗餘的。一個簡單的 global event bus 就足夠您所需了。可是,若是您須要構建一箇中大型單頁應用,您極可能會考慮如何更好地在組件外部管理狀態,Vuex 將會成爲天然而然的選擇。vuex

5、Vuex安裝(限定開發環境爲 vue-cli)

首先要安裝vue-cli腳手架,對於大陸用戶,建議將npm的註冊表源設置爲國內的鏡像(淘寶鏡像),能夠大幅提高安裝速度。vue-cli

npm config set registry https://[registry.npm.taobao.org](http://registry.npm.taobao.org/)
npm config get registry//配置後可經過下面方式來驗證是否成功
npm install -g cnpm --registry=[https://registry](https://registry/).npm.taobao.org
//cnpm安裝腳手架
cnpm install -g vue-cli
vue init webpack my-vue
cd my-vue
cnpm install
cnpm run dev

腳手架安裝好後,再安裝vuexnpm

cnpm install vuex --save

6、如何使用Vuex

1.如何經過Vue來實現以下效果?

image

這個小demo很容易用vue實現,核心代碼以下:後端

<div class="hello">
    <p>click {{count}} times,count is {{evenOrOdd}}</p>
    <button @click="increment">+</button>
    <button @click="decrement">-</button>
    <button @click="incrementIfOdd">increment if odd</button>
    <button @click="incrementAsync">increment async</button>
  </div>
  ......
  export default {
  name: "HelloWorld",
  data() {
    return {
      count: 0
    };
  },
  computed: {
    evenOrOdd() {
      return this.count % 2 === 0 ? "偶數" : "奇數";
    }
  },
  methods: {
    increment() {
      this.count = this.count + 1;
    },
    decrement() {
      this.count = this.count - 1;
    },
    // 只有是奇數才加1
    incrementIfOdd() {
      if (this.count % 2 === 1) {
        this.count = this.count + 1;
      }
    },
    // 過兩秒才加1
    incrementAsync() {
      setInterval(() => {
        this.count = this.count + 1;
      }, 2000);
    }
  }
}

2.如何經過Vuex來改造上面代碼?

①建立一個store.js文件

import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const store = new Vuex.Store({
    state: {
        count: 0
    },
    mutations: {// 包含了多個直接更新state函數的對象
        INCREMENT(state) {
            state.count = state.count + 1;
        },
        DECREMENT(state) {
            state.count = state.count - 1;
        }
    },
    getters: {   // 當讀取屬性值時自動調用並返回屬性值
        evenOrOdd(state) {
            return state.count % 2 === 0 ? "偶數" : "奇數";
        }
    },
    actions: { // 包含了多個對應事件回調函數的對象
        incrementIfOdd({ commit, state }) { // 帶條件的action
            if (state.count % 2 === 1) {
                commit('INCREMENT')
            }
        },
        incrementAsync({ commit }) { //異步的action
            setInterval(() => {
                commit('INCREMENT')
            }, 2000);
        }

    }
})
export default store //用export default 封裝代碼,讓外部能夠引用

②在main.js文件中引入store.js文件

import store from './store'
new Vue({
  el: '#app',
  router,
  store,//註冊上vuex的store: 全部組件對象都多一個屬性$store
  components: { App },
  template: '<App/>'
})

③新建一個模板HelloWorld.vue

<template>
  <div class="hello">
    <p>click {{count}} times,count is {{evenOrOdd}}</p>
    <button @click="increment">+</button>
    <button @click="decrement">-</button>
    <button @click="incrementIfOdd">increment if odd</button>
    <button @click="incrementAsync">increment async</button>
  </div>
</template>
<script>
export default {
  name: "HelloWorld",
  computed: {
    count() {
      return this.$store.state.count;
    },
    evenOrOdd() {
      return this.$store.getters.evenOrOdd;
    }
  },
  methods: {
    increment() {
      this.$store.commit("INCREMENT");
    },
    decrement() {
      this.$store.commit("DECREMENT");
    },
    // 只有是奇數才加1
    incrementIfOdd() {
      this.$store.dispatch("incrementIfOdd"); //觸發store中對應的action調用
    },
    // 過兩秒才加1
    incrementAsync() {
      this.$store.dispatch("incrementAsync");
    }
  }
};
</script>

因爲 store 中的狀態是響應式的,當 Vue 組件從 store 中讀取狀態的時候,若 store 中的狀態發生變化,那麼相應的組件也會相應地獲得高效更新。在組件中調用 store 中的狀態簡單到僅須要在計算屬性中返回便可。改變store 中的狀態的惟一途徑就是顯式地提交 (commit) mutations。

3.如何通mapState等輔助函數優化上面代碼?

import { mapActions, mapGetters, mapState, mapMutations } from "vuex";
...
 computed: {
    ...mapState(["count"]),
    ...mapGetters(["evenOrOdd"])
    }
  methods: {
    ...mapActions(["incrementIfOdd", "incrementAsync"]),
    ...mapMutations(["increment", "decrement"])
    }

有點必需要注意:HelloWorld.vue文件中increment函數名稱要跟store.js文件mutations中一致,才能夠寫成 ...mapMutations(["increment", "decrement"]),一樣的道理,incrementIfOdd和incrementAsync也要和store.js文件actions保持一致。

7、使用Vuex的注意點

1.如何在Mutations裏傳遞參數

先store.js文件裏給add方法加上一個參數n

mutations: {
    INCREMENT(state,n) {
      state.count+=n;
    },
    DECREMENT(state){
        state.count--;
    }
  }

而後在HelloWorld.vue裏修改按鈕的commit( )方法傳遞的參數

increment() {
      return this.$store.commit("INCREMENT",2);
    },
 decrement() {
      return this.$store.commit("DECREMENT");
    }

2.如何理解getters

getters從表面是得到的意思,能夠把他看做在獲取數據以前進行的一種再編輯,至關於對數據的一個過濾和加工。getters就像計算屬性同樣,getter 的返回值會根據它的依賴被緩存起來,且只有當它的依賴值發生了改變纔會被從新計算。

例如:要對store.js文件中的count進行操做,在它輸出前,給它加上100。

首先要在store.js裏Vuex.Store()裏引入getters

getters:{
     count:state=>state.count+=100
  }

而後在HelloWorld.vue中對computed進行配置,在vue 的構造器裏邊只能有一個computed屬性,若是你寫多個,只有最後一個computed屬性可用,因此要用展開運算符」…」對上節寫的computed屬性進行一個改造。

computed: {
   ...mapGetters(["count"])
}

3.actions和mutations區別

actions和上面的Mutations功能基本同樣,不一樣點是,actions是異步的改變state狀態,而Mutations是同步改變狀態

同步的意義在於這樣每個 mutation 執行完成後均可以對應到一個新的狀態(和 reducer 同樣),這樣 devtools 就能夠打個 snapshot 存下來,而後就能夠隨便 time-travel 了。若是你開着 devtool 調用一個異步的 action,你能夠清楚地看到它所調用的 mutation 是什麼時候被記錄下來的,而且能夠馬上查看它們對應的狀態----尤雨溪

ps:若是想訪問源代碼,請猛戳git地址

若是以爲文章對你有些許幫助,歡迎在個人GitHub博客點贊和關注,感激涕零!

參考文章

vuex官方文檔

Vuex 2.0 源碼分析

相關文章
相關標籤/搜索