1.前言
在使用Vue進行開發的時候,關於vue組件通信的方式,除了通俗易懂了解Vue組件的通信方式這篇博文談到三種通信方式,其實vue更提倡我們使用vuex來進行組件間的狀態管理以及通信問題。Vuex 是一個專為 Vue.js 應用程序開發的狀態管理模式。
2.引例
在學習vuex之前,我們不妨先看這樣一個例子:
在一個父子組件嵌套環境下,父組件可以改變子組件的背景顏色,而子組件自身也可以改變自己的背景顏色。
雖然使用之前談到的組件通信方式也可以實現這個例子,但是這不是我們今天想討論的。
我們可以換個角度看這個例子:
我們可以把子組件的背景顏色看做一種狀顏色態,我們把這個顏色狀態放在組件的外部,讓子組件去讀取這個狀態來決定自己的背景顏色該是什么,如果父組件需要改變子組件的背景顏色,那么只需讓父組件去改變這個狀態即可,因為子組件的背景顏色取決於這個顏色狀態是什么顏色,控制了這個狀態,即控制了子組件的背景顏色。
簡而言之一句話:就是需要對狀態進行管理,這也就是vuex的核心:狀態管理。
3.store
vuex對於狀態管理提供了狀態倉庫store,store的作用就是將所有狀態統一存儲管理起來,狀態倉庫store是響應式的,當 Vue 組件從 store 中讀取狀態的時候,若 store 中的狀態發生變化,那么相應的組件也會相應地得到高效更新。說了這么多,我們簡單使用一下store。
3.1 安裝vuex
在使用vue-cli進行模塊化開發時,使用vuex之前必須先安裝vuex:
npm install vuex --save
3.2 創建狀態倉庫store
安裝完成后,在項目中main.js文件中按照如下方式引入vuex並創建一個狀態倉庫store,在狀態倉庫store內創建第一個狀態color並設置該狀態初始值為red:
1 import vuex from 'vuex' 2 Vue.use(vuex); 3 var store = new vuex.Store({ 4 state:{ 5 color:'red' 6 } 7 })
然后,在main.js中的根vue對象中掛載上面創建好的狀態倉庫store,這樣在任何組件中都可以去讀取倉庫中的狀態:
1 new Vue({ 2 el: '#app', 3 store, 4 render: h => h(App) 5 })
OK,到這里,狀態倉庫和狀態就創建好了,接下來我們就可以采用讀取狀態和改變狀態的方式來改寫上面的例子了,代碼如下:
父組件代碼:
<template> <div id="app"> <h1>我是父組件</h1> <!--點擊按鈕后,去狀態倉庫將狀態color改成yellow--> <input type="button" value="變成黃色" @click="$store.state.color = 'yellow'"> <Child></Child> </div> </template> <script> import Child from './Child' export default { name: 'app', components:{ Child }, data () { return { } }, } </script> <style> </style>
子組件代碼:
<template> <!--子組件的backgroundColor屬性值取決於狀態倉庫中的狀態color值--> <div class="child" :style="{backgroundColor:$store.state.color}"> <h1>子組件</h1> <!--子組件點擊按鈕,也可以去狀態倉庫中改變color狀態值--> <input type="button" value="變成藍色" @click="$store.state.color = 'blue'"> </div> </template> <script> export default { name: "Child", } </script> <style scoped> .child{ width: 500px; height: 500px; } </style>
效果如下:
以上例子就是Vuex最簡單的使用,怎么樣,是不是so easy!!
3.3 優化代碼結構
前面為了方便 , 我們把 store 對象寫在了 main.js 里面 , 但實際上為了便於日后的維護 , 我們將store單獨隔離出來分開寫更好 , 我們在 src
目錄下 , 新建一個 store
文件夾 , 然后在里面新建一個 index.js
:
import Vue from 'vue' import vuex from 'vuex' Vue.use(vuex); export default new vuex.Store({ state:{ color:'red' } })
那么相應的 , 在 main.js 里的代碼應該改成 :
import Vue from 'vue' import App from './App.vue' //引入store import store from './store' new Vue({ el: '#app', store, render: h => h(App) })
這樣就把 store 單獨分離出去了 ,日后當狀態越來越多的時候維護起來就更加方便啦!
4.module
雖然我們把 store 單獨分離出去了,但是隨着項目規模越來越大,組件也越來越多,這就造成store.state里面的狀態會越來越多,這么多的狀態都堆在store.state里面顯然不是一個很好代碼結構,更重要的是哪些組件依賴哪些狀態也會變得非常不清晰。那有沒有什么更好的解決方案呢?其實Vuex早就替我們想到了,Vuex中的modules就是用來解決這個問題的。
modules可以將store.state里面的狀態按照各個組件所依賴的情況進行分類,例如,我們將上面例子中子組件Child所依賴的color狀態單獨寫進一個js文件中,然后在store 文件夾下的 index.js
中引入即可,代碼如下:
//在store文件夾下新建Child_store.js文件: export default { state:{ color:'red' } }
然后在store 文件夾下的 index.js
中引入:
import Vue from 'vue' import vuex from 'vuex' Vue.use(vuex); import Child_store from './Child_store.js';//引入Child_store.js中的store對象 export default new vuex.Store({ modules: { Child_store:Child_store } })
做出這樣的修改之后 , 我們只需將之前我們使用的 $store.state.color 統統改為 $store.state.Child_store.color即可。
如果還有其他的組件所依賴的狀態 ,那就在store文件夾下再新建一個對應的xx_store.js文件 , 然后將他們引入到 index.js 文件中的modules中。
import Vue from 'vue' import vuex from 'vuex' Vue.use(vuex); import Child_store from './Child_store.js';//引入Child_store.js中的store對象 import xxx_store from './xxx_store.js';//引入xxx_store.js中的store對象 export default new vuex.Store({ modules: { Child_store:Child_store, xxx_store :xxx_store } })
5. mutation
在上面例子中,我們點擊按鈕這個動作僅僅依賴了一個color狀態,所以我們可以將改變狀態的動作直接寫在按鈕的click的事件中,即@click="$store.state.Child_store.color = 'blue'"。,但是如果按鈕點擊依賴了多個狀態,那么我們就不能把所有狀態改變的動作都寫在click事件中,當然,也不允許我們這么干。此時我們就需要用到mutations了,mutations允許我們一次性更新多個狀態。
例如,我們給上面例子中的子組件Child再新增一個狀態width,用來改變子組件的寬度,我們希望當點擊按鈕,既改變了子組件的背景顏色,而且也改變子組件的寬度,代碼如下:
1.給Child_store.js中增加mutations選項
//Child_store.js export default { state:{ color:'red', width:'500px' }, mutations:{ switch(state,payload){ //這里的state對應着上面那個state state.color = payload.color; state.width = payload.width; } }, }
在mutations選項中定義switch函數,該函數可以接收兩個參數,第一個參數必須是state,即為要改變的狀態對象,第二個參數稱為載荷(payload),即函數內要使用的參數.
2.子組件代碼
<template> <div class="child" :style="{backgroundColor:$store.state.Child_store.color,width:$store.state.Child_store.width}"> <h1>子組件</h1> </div> </template> <script> export default { name: "Child", } </script> <style scoped> .child{ height: 500px; } </style>
3.父組件代碼
<template> <div id="app"> <h1>我是父組件</h1> <input type="button" value="變成藍色並且寬度變小" @click="$store.commit('switch',{color:'blue',width:'100px'})"> <Child></Child> </div> </template> <script> import Child from './Child' export default { name: 'app', components:{ Child }, data () { return { } } } </script> <style> </style>
父組件中使用$store.commit('switch',{color:'blue',width:'100px'})來觸發mutations中的switch函數,$store.commit()接收的第一個參數為要觸發的mutations中的哪個函數,第二個參數是一個對象,也就是要觸發的switch函數中的載荷payload。
效果如下:
6.action
多個state的操作 , 使用mutations會來觸發會比較好維護 , 那么需要執行多個mutations就需要用action了:
1.給Child_store.js中增加actions選項
export default { state:{ color:'red', width:'500px' }, mutations:{ switch(state,payload){ //這里的state對應着上面那個state state.color = payload.color; state.width = payload.width; } }, actions:{ switch_action(ctx,payload){ //這里的ctx和我們使用的$store擁有相同的對象和方法 ctx.commit('switch',payload); //你還可以在下面觸發其他的mutations方法 } } }
2.子組件不用變,在父組件中需要使用$store.dispatch('switch_action',{color:'blue',width:'100px'})來觸發actions中的switch_action方法
<template> <div id="app"> <h1>我是父組件</h1> <input type="button" value="變成藍色並且寬度變小" @click="$store.dispatch('switch_action',{color:'blue',width:'100px'})"> <Child></Child> </div> </template> <script> import Child from './Child' export default { name: 'app', components:{ Child }, data () { return { } } } </script> <style> </style>
7.getter
有時候我們需要從state 中派生出一些狀態,也就是說,某些狀態是通過現有的一個或多個狀態通過計算得到的,類似於計算屬性computed。假如我們現有一個狀態show:true,但是我們很多地方又需要一個跟show狀態相反的notShow狀態,這種情況放在以前,我們會寫一個計算屬性來返回notShow:
computed(){ not_show(){ return !this.$store.state.show; } }
哪個組件里需要notShow狀態,就在哪個組件里寫一遍上面的代碼,如果需要這個狀態的組件很多,那么就需要寫很多遍重復的代碼。這肯定不是我們想要的,而Vuex中的getter就很好的解決了這個問題:
export default { state:{ show:false }, getters:{ notShow(state){//這里的state對應着上面這個state return !state.show; } } }
我們在組件中使用$store.state.show來獲得狀態show,類似的 , 我們可以使用$store.state.notShow來獲得狀態notShow。
注意 $store.state.notShow的值是不能直接修改的 , 需要對應的 state 發生變化才能修改。
8.總結
了解了以上這些,再回頭去看Vuex的官方文檔,就應該不會一臉懵逼啦,哈哈哈哈。
(完)