1. 程式人生 > >VUE探索第四篇-VUEX

VUE探索第四篇-VUEX

一、什麼是VUEX

    元件化是VUE的核心功能,元件間的資料共享是我們開發過程中經常遇到的情況,在元件較少的情況下,可以通用公共變數等解決,一旦應用中元件數量大,且元件間相關巢狀,關係複雜,想要維護好這些公共變數,將是一件非常痛苦的事。

   瞭解spring的同學會熟悉IOC的概念,初始化時,為相關的類建立全域性唯一的單例物件,通過注入的方式,統一管理,處處使用。VUEX要做的事如spring較類似,簡單的說,就是將這些共享的資料或狀態,構建成全域性的單例模式進行集中管理。

總體來說,vuex的使用非常簡單,主要了解以下幾個概念

  • State
  • Mutations
  • Actions
  • Getters

下面我們結合專案實踐,介紹下這幾個概念以及使用。 

二、VUEX環境搭建

首先需要安裝vuex的環境。

npm install vuex --save

在main.js中引入vue

import Vue from 'vue'
import App from './App'
import router from './router'
import Vuex from 'vuex'
Vue.use(Vuex);

我們模擬微信的聊天過程,如下:

三、State

State是vuex的單一狀態樹,儲存各元件共享的資料來源,它將會包含在store例項中。

我們來分析下這個聊天介面,"下里巴人"和"陽春白雪"分別建立兩個聊天介面元件DialogA和DialogB,兩者的對話儲存到變數msg中,而變數msg作為共享資料來源由state維護。

main.js中建立store例項,並在根例項中註冊store。

const store = new Vuex.Store({
  state:{
	msg:"",
  }
})

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

接下來我們就在聊天介面中使用變數msg儲存和展示對話。

先建立template,分成三部分,對話展示區域,訊息輸入框以及傳送按鈕。

<template>
  <div class="hello">
    <!--展示區域-->
    <div class="dialog" v-html="$store.state.msg">
    </div>
    <!--訊息輸入框-->
    <input type="" name="dialog" v-model="sendMessage" placeholder="請輸入訊息" style="width:200px">
    <!--傳送按鈕-->
    <button  @click="send_msg"  >傳送</button>
   </div>
</template>

對話方塊展示區域,使用v-html繫結state的msg狀態變數。

輸入訊息後,點擊發送,響應send_msg方法,獲取輸入的訊息文字this.sendMessage,並賦值給msg(注意元件中需要使用this.$store.state.msg,才能訪問到變數)。

  methods:{
   send_msg:function(){
    if(this.sendMessage==""){
        return
    }
    this.$store.state.msg+="<div class='showmsg'><img src='/static/mail.jpg'></img> "+this.sendMessage+"</div>";
    this.sendMessage="";
  
   }
  }

然後賦值給revMessage,此時就可以同步看到自己傳送的訊息。

那陽春白雪如何接受到訊息呢,由於msg是共享,故也會通過v-html同步更新。

實現的效果如下:

聊天

四、Mutation、Action

   由上可知,元件訪問state的狀態變數,需要通過this.$store.state.xxx,如果state中的變數和層級較多,特別是一些操作要依賴多種狀態的運算,使用會極不方便;另外,我們更希望這些狀態變數不要直接暴露給元件,而提供一些方法和介面供元件呼叫,這樣更安全,更加解耦。而Mutation就能解決這些問題,我們來看下。

在store例項中,新增mutations模組

const store = new Vuex.Store({
  state:{
	msg:"",
  },
  mutations:{
  	    //回撥函式中,傳入兩個參加,state,payload
        send_msg(state,payload){
            state.msg+="<div class='showmsg'><img src='/static/mail.jpg'></img> "+payload.sendMessage+"</div>"
        }
    }
})

mutations新增一個回撥方法send_msg,這個方法實現的核心功能與上面的send_msg類似,它有兩個入參,state表示當前的狀態樹,payload為自定義的負荷物件,這裡傳入輸入的文字訊息sendMessage。

該方法並不是直接呼叫,而是採用commit提交來觸發。

methods:{
   send_msg:function(){
    if(this.sendMessage==""){
        return
    }
    //採用commit方式提交
    this.$store.commit('send_msg', {sendMessage: this.sendMessage});
    this.sendMessage="";

   }
  }

注意:mutations的回撥中的操作只能是同步的。

Action與Mutation類似,多個state使用mutation進行操作維護,那麼多個mutation就用Action進行操作維護。action中可以使用非同步呼叫方法。

我們繼續增加actions模組。

const store = new Vuex.Store({
  state:{
	msg:"",
  },
  mutations:{
  	    //回撥函式中,傳入兩個參加,state,payload
        send_msg(state,payload){
            state.msg+="<div class='showmsg'><img src='/static/mail.jpg'></img> "+payload.sendMessage+"</div>"
        }
    },
  actions:{
   	send_msg(context,payload){
   		context.commit('send_msg',payload);
   	}
   }
})

action是通過dispatch進行分發,

  methods:{
   send_msg:function(){
    if(this.sendMessage==""){
        return
    }
    //採用commit方式提交
    //this.$store.commit('send_msg', {sendMessage: this.sendMessage});
    this.$store.dispatch('send_msg', {sendMessage: this.sendMessage});
    this.sendMessage="";
   }
  }

五、Module

   在大型vue應用中,如果只有一個store檔案儲存狀態變數,會使檔案變得臃腫,也不利於協同開發的,module支援模組化開發,根據業務邏輯將store模組化各個物件,在store中組裝起來,其格式如下:

const moduleA = {
  state: { ... },
  mutations: { ... },
  actions: { ... },
  getters: { ... }
}

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

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

上面以實現了基本的聊天過程,但是和待實現的樣式還是有差別的,我們需要區別別人的記錄和自己的記錄,並在顯示的位置和背景色上有所標識。我們分隔針對兩者物件成兩個module,每個物件維護自己的對話記錄。如下:

新建一個store資料夾,分別建立三個子檔案,分別是dialoga.js,dialogb.js,index.js。

在dailoga.js中我們建立"下里巴人"對應的狀態數模組。

export default {
	namespaced: true,
	state:{
		msg:''
	},
    mutations:{
  	    //回撥函式中,傳入兩個參加,state,payload
        send_msg(state,payload){
            state.msg+="<div class='showmsgright'><div>"+payload.sendMessage+"</div><img src='/static/mail.jpg'></img>	</div>"
        },
        rev_msg(state,payload){
        	state.msg+="<div class='showmsgleft'><img src='/static/femail.jpg'></img><div> "+payload.sendMessage+"</div></div>"
        }
    },
    actions:{
   	   send_msg(context,payload){

   		context.commit('send_msg',payload);
   	   },
   	   rev_msg(context,payload){
   		context.commit('rev_msg',payload);
   	   },
   }
}

此模組中的msg將儲存"下里巴人"的聊天記錄。namespace表示該模組啟用名稱空間,send_msg處理自己對話方塊的展示,rev_msg傳送訊息給對方,並在對方對話方塊展示。dialogb.js與該檔案類似。

在index.js中引入兩個模組檔案,並匯出store例項物件。

import Vue from 'vue'
import Vuex from 'vuex'
import dialoga from './dialoga.js'
import dialogb from './dialogb.js'
Vue.use(Vuex);

export default new Vuex.Store({
	modules:{
		dialoga,
		dialogb
	}
})

在man.js中,引入store,並註冊。

import Vue from 'vue'
import App from './App'
import router from './router'
import store from './store'
import Vuex from 'vuex'
Vue.use(Vuex);

Vue.config.productionTip = false

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

修改對話方塊元件中的傳送方法。

methods:{
   send_msg:function(){
    if(this.sendMessage==""){
        return
    }
    //自有對話方塊展示訊息
    this.$store.dispatch('dialoga/send_msg', {sendMessage: this.sendMessage});
    //傳送給對方,並展示
    this.$store.dispatch('dialogb/rev_msg', {sendMessage: this.sendMessage});
    this.sendMessage="";
   }
  }

注意,此時的回撥方法是帶有路徑的,表示呼叫哪個模組的方法,此種寫法需要在配合module的名稱空間使用。

六、Getters

Getter可以理解為store中的計算屬性,它可以對state的狀態資料進行篩選和重新計算,提供給元件使用,比如說我們要統計每個人傳送訊息的次數,在state中增加count屬性。

state:{
		msg:'',
		count:0
	}

同時增加Getters模組

getters:{
       count:state=>{
       	return state.count++;
       }
   }

在元件中通過this.$store.getters.count呼叫。

七、輔助函式:mapState、mapGetters、mapActions、mapMutations

一般情況下,我們在元件中使用$store.state.xxx訪問狀態屬性,比較繁雜,vuex提供了相關的輔助方法簡化寫法。我們來改寫下

在元件頁面中引入mapstate

import {mapState} from 'vuex';

建立mapSate

computed:{
    ...mapState({
      msg: state =>state.dialoga.msg
      })
  }

在模板中直接使用該變數

<div class="dialog" v-html="msg">

其他的幾個輔助函式類似,注意,mapActions與mapMutations寫在method中,mapSate與mapGetters要寫在computed中。