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中。