閑話Vuex
前言:為什么要Vuex
在Vue項目中, 功能組件和服務組件的封裝都會有各種各樣的數據傳遞,用 props 定義字段或者是子組件 emit 來通信。但是,當我們的項目的復雜度逐漸增長的時候,組件會越來越多,而且一些組件并不存在調用關系,一些數據需要共享的時候,那么問題就來了:
傳參的方法對于多層嵌套的組件將會非常繁瑣,并且對于兄弟組件間的狀態傳遞無能為力;
采用父子組件直接引用或者通過事件來變更和同步狀態的多份拷貝,代碼冗余,慢慢的難以維護。
所以我們在項目中經常是全局定義一個實例“全局單例模式”,當然你也可以定義多個實例來共享一個數據源。
比如說我們有一個系統,包含左側菜單組件以及右側內容區域組件兩部分。現在需要在內容區域給系統做一個全屏的功能,即點擊右側按鈕時菜單隱藏。如果我們不使用Vuex,首先我們要在內容組件中使用 $emit 改變父組件的內容,然后將改變后的值傳遞左側菜單組件控制隱藏。而現在我們有了 Vuex,菜單組件和內容組件共享某個狀態變量,需要隱藏時只需要在內容組件中改變狀態,即可在菜單組件中共享到相應的變量。
Vuex 就是為了解決這些問題而生。
Vue核心
state:驅動應用的數據源,類似于?vue?中的?data,是一棵單一狀態樹,涉及到的狀態變量都存放在?state?上。
getter:store?的計算屬性。類似于?vue?中的?computed?計算屬性。當有多個組件對某個狀態引用后過濾時,需要在多個組件內部使用?computed?進行計算,但有了?Getter?后我們可以將這個計算提前到?Vuex?中,一次計算多處使用。例如下面的代碼我們要過濾出啟用(狀態為?1)狀態的數據:
mutation:類似于?vue?中的方法,當我們需要改變?Vuex?中的某個變量時需要用到。
例如我們前面提到的顯示隱藏菜單:
一般?mutations?中方法接受的第一個參數為?state,后面的參數則是傳遞的值。例如我們需要顯示菜單則在組件中調用:
this.$store.commit('onCollapse',?true);
這里改變值需要通過?commit(‘方法名’,?’…參數’)調用。
Action:? 類似于?mutation,但是改變狀態時只能通過提交?mutation,而不能直接改變。同時?action?可以包含異步的操作,?而?mutation?是不允許包含異步操作的。
Vuex 模塊分割
由于 state 使用的是單一狀態樹,在項目較大時會導致 Vuex 變得臃腫且不易維護,這時候我們需要對項目進行分模塊化處理,每一個模塊都有自己的 state、mutation、action、getter:
Modules
- app.js
- user.js
index.js
最后統一在?inde.js?中導出:
import?Vue?from?'vue'
import?vuex?from?'vuex'
Vue.use(vuex);
import?app?from?'./modules/app'
import?user?from?'./modules/user'
const?store?=?new?vuex.Store({
modules:?{
app:?app,
user:?user
}
})
export?default?store
Vuex 輔助函數
當我們的項目調用比較簡單時輔助函數可能不會起到很大的作用,但當項目調用復雜時,這些輔助函數(mapState、mapGetters、mapMutations、mapActions)能大大提高開發效率,使得代碼更加簡潔優雅。
其中?mapState、mapGetters?是對變量的獲取,放在組件的?computed?計算屬性中即可,如下:
computed:?{
...mapState({
themeColor:?state?=>?state.app.themeColor,
sysName:?state?=>?state.app.appName,
appHomeRouter:?state?=>?state.app.appHomeRouter,
})
}
而?mapMutations、mapActions?是對方法的調用,需要放在組件的?method?中,如下:
methods:?{
...mapMutations({
changeStatus:?'changeStatus'
}),
changeStatus(){
this.changeStatus()//直接調用?mapMutations?中的方法
//?this.$store.commit('changeStatus')//不使用輔助函數時的調用
},
...mapActions({
numAdd:?'actionNumAdd'
}),
actionnum6(){
this.numAdd()//直接調用?mapActions?中的方法
//?this.$store.dispatch('actionNumAdd')//不使用輔助函數時的調用
},
}
情景案例
有時候我們終于完成了一份試卷,可能由于手滑不小心把瀏覽器給關閉或者刷新了一下,導致辛辛苦苦做完的題目又得重頭來過,而?Vuex?結合?Map?同樣可以解決這個問題,核心思想就是將已經做過的題目更新到?Vuex?中。
講到這里肯定會有人存在疑問:Vuex?中存儲的狀態變量在瀏覽器關閉或者刷新時會重新初始,這樣如何能在瀏覽器刷新的時候獲取到存儲的值呢?這里我們需要引進緩存——sessionStorage。
首先在?state?中定義一個?answerMap:
state:?{
answerMap:?sessionStorage.getItem("answerMap")
}
然后在每次做題完成后將答案存儲到?sessionStorage?中,即:
const?mutations?=?{
setAnswerMap(state,?answer)?{
let?map?=?JSON.parse(state.answerMap);
if?(map?==?null?||?!(map?instanceof?Map))?{
map?=?new?Map();
}
map.set(answer.id,?answer);
sessionStorage.setItem("answerMap",?JSON.stringify(map))
},
}
正常情況下到這里就結束了,但是實際操作后會發現每次獲取到的?answerMap?總是為?null,原因在于最后一行代碼:
sessionStorage.setItem("answerMap",?JSON.stringify(map))
不能直接將?map?通過?JSON.stringify()?進行轉換,需要先將?map?轉成?obj?對象后再轉成?json?對象,將?map?轉成?obj:
let?obj?=?Object.create(null);
for?(let?[k,?v]?of?map)?{
obj[k]?=?v;
}
此時在獲取則解決為?null?的情況,完善后代碼如下:
const?mutations?=?{
setAnswerMap(state,?answer)?{
let?map?=?JSON.parse(state.answerMap);
if?(map?==?null?||?!(map?instanceof?Map))?{
map?=?new?Map();
}
map.set(answer.id,?answer);
let?obj?=?Object.create(null);
for?(let?[k,?v]?of?map)?{
obj[k]?=?v;
}
sessionStorage.setItem("answerMap",?JSON.stringify(obj))
},
}
這里借助了?Vuex?+?map?+?sessionStorage?防止已做題目丟失的情況,當然只是提供一種解決問題的思路,可能在真實場景中會更加復雜,需要根據不同的場景靈活運用。比如說我們如果需要在手動清除瀏覽器緩存、甚至服務器奔潰后依舊能獲取到已做的題目則使用?Vuex?明顯不合適,此時就需要利用?Redis?緩存來處理。
在實際項目中可能會遇到更加復雜的情況,但是萬變不離其宗,在掌握基礎的情況下總會有一種思路能夠更好的解決問題。
Vue 數據結構
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。