天天看點

vuex 深入了解

1、問題

可以想象到在簡單的 ​<code>​父子​</code>​,​<code>​子父​</code>​ 元件之間的通信是很輕松的,通過 ​<code>​props​</code>​ 和 ​<code>​events​</code>​ 即可實作;但是往往我們的應用可能不隻有這麼簡單的層級關系,在多層跨級元件如果通過 ​<code>​props​</code>​ 去傳遞,那意味着一層一層的往子元件傳遞,最終你可能不知道目前元件的資料最終來自哪個父元件(當然你可以逆着方向一層一層往上找),通過 ​<code>​events​</code>​ 事件機制顯然也存在着類似的問題。如果你覺得這樣也可以接受,你可能不需要 Vuex;但如果你在想有沒有什麼好的模式優雅的去解決,你可以繼續閱讀下面的部分。

2、vuex

vue提供了另外一個類似 Redux 的解決方案 Vuex,一個集中式狀态管理的庫;也就是說,你可能不需要 Vuex,它隻是對你應用狀态進行管理的一個庫。

vuex關鍵詞:

集中式狀态管理模式(注意是強調管理應用的所有元件的狀态)

可預測(前提是以相應的規則作為保證)

3、集中式狀态管理模式

在說集中式管理模式之前,我們可以先來想想常見的處理方式是怎樣的,即每個元件維護自身的資料和狀态,自給自足,分而治之;其思路大緻如下:

定義元件自身的初始資料

在元件内擷取異步資料

根據資料渲染更新視圖

但事情并非那麼完美,由于這種方式封裝的元件的内部實作聚合了異步請求的資料和自身的狀态,真正組裝複用起來是存在一定問題的。比如:

在同一可視區域的備援請求數

不同層級元件的資料共享問題

集中式狀态管理模式則以一個全局單例模式管理應用的狀态,類似于全局對象,但不完全一樣。

Vuex 的狀态管理存儲是響應式的:就是當你的元件使用到了 Vuex 的某個狀态,一旦它發生改變了,所有關聯的元件都會自動更新相對應的資料。

不能直接修改 Vuex 的狀态:修改 Vuex 的狀态唯一途徑是送出(commit) mutations 來實作修改。

vuex 深入了解

Vue Components:Vue元件。HTML頁面上,負責接收使用者操作等互動行為,執行dispatch方法觸發對應action進行回應。

dispatch:操作行為觸發方法,是唯一能執行action的方法。

actions:操作行為處理子產品。負責處理Vue Components接收到的所有互動行為。包含同步/異步操作,支援多個同名方法,按照注冊的順序依次觸發。向背景API請求的操作就在這個子產品中進行,包括觸發其他action以及送出mutation的操作。該子產品提供了Promise的封裝,以支援action的鍊式觸發。

commit:狀态改變送出操作方法。對mutation進行送出,是唯一能執行mutation的方法。

mutations:狀态改變操作方法。是Vuex修改state的唯一推薦方法,其他修改方式在嚴格模式下将會報錯。該方法隻能進行同步操作,且方法名隻能全局唯一。操作之中會有一些hook暴露出來,以進行state的監控等。

state:頁面狀态管理容器對象。集中存儲Vue components中data對象的零散資料,全局唯一,以進行統一的狀态管理。頁面顯示所需的資料從該對象中進行讀取,利用Vue的細粒度資料響應機制來進行高效的狀态更新。

getters:state對象讀取方法。圖中沒有單獨列出該子產品,應該被包含在了render中,Vue Components通過該方法讀取全局state對象。

流程:Vue元件接收互動行為,調用dispatch方法觸發action相關處理,若頁面狀态需要改變,則調用commit方法送出mutation修改state,通過getters擷取到state新值,重新渲染Vue Components,界面随之更新

集中式存儲管理應用的所有狀态,按照字面意思是将所有的狀态都集中式管理,也就是存到 Vuex 的全局單一 store 中,顯然我們是不能這樣去了解的,應該視應用場景而定的,大緻也可以分為以下幾種:

對于使用者輸入的狀态,比如控制模态框的顯示隐藏,我們一般在元件内處理消化;對于需要需要跨元件通信的,則可以存儲在全局的 store 中,我們可以将這一類狀态稱之為本地狀态(local state)。

對于服務端傳過來的資料狀态,按照大多數的實踐是存儲在全局的 store 中,這樣可以在任意的元件中都可以使用;當然,也可以隻将多元件的共享的資料存儲在全局的 store 中,單個元件需要的資料内部處理消化,元件銷毀時對應的資料狀态也會銷毀。

說明:

(1)可以直接操作state

&lt;a href="javascript:;" @click="$store.state.show = true"&gt;點選&lt;/a&gt;

在嚴格模式下,無論何時發生了狀态變更且不是由 mutation 函數引起的,将會抛出錯誤。

(2)如果在 mutations 裡執行異步操作會發生什麼事情 , 實際上并不會發生什麼奇怪的事情 , 隻是官方推薦 , 不要在 mutationss 裡執行異步操作而已。

(3)多個 state 的操作 , 使用 mutations 會來觸發會比較好維護 , 那麼需要執行多個 mutations 就需要用 action 了

(4)很多時候 , $store.state.dialog.show 、$store.dispatch('switch_dialog') 這種寫法又長又臭 , 很不友善 , 我們沒使用 vuex 的時候 , 擷取一個狀态隻需要 this.show , 執行一個方法隻需要 this.switch_dialog 就行了 , 使用 vuex 使寫法變複雜了 ?

 ​<code>​mapState​</code>​ 函數傳回的是一個對象。

(5)、getter:傳回值會根據它的依賴被緩存起來,且隻有當它的依賴值發生了改變才會被重新計算。

(6)、​<code>​mapGetters​</code>​ 輔助函數僅僅是将 store 中的 getter 映射到局部計算屬性:

 (7)、使用常量替代 Mutation 事件類型

(8)​<code>​mapMutations​</code>​ 

你可以在元件中使用 ​<code>​this.$store.commit('xxx')​</code>​ 送出 mutation,或者使用 ​<code>​mapMutations​</code>​ 輔助函數将元件中的 methods 映射為 ​<code>​store.commit​</code>​ 調用(需要在根節點注入 ​<code>​store​</code>​)。

(9)Action 

Action 函數接受一個與 store 執行個體具有相同方法和屬性的 context 對象,是以你可以調用 ​<code>​context.commit​</code>​送出一個 mutation,或者通過 ​<code>​context.state​</code>​ 和 ​<code>​context.getters​</code>​ 來擷取 state 和 getters。當我們在之後介紹到 ​​Modules​​ 時,你就知道 context 對象為什麼不是 store 執行個體本身了。

寫法一:

參數解構寫法:

(10)​<code>​mapActions​</code>​ 

你在元件中使用 ​<code>​this.$store.dispatch('xxx')​</code>​ 分發 action,或者使用 ​<code>​mapActions​</code>​ 輔助函數将元件的 methods 映射為 ​<code>​store.dispatch​</code>​ 調用(需要先在根節點注入 ​<code>​store​</code>​):

(11)module

對于子產品内部的 mutation 和 getter,接收的第一個參數是子產品的局部狀态對象。

同樣,對于子產品内部的 action,局部狀态通過 ​<code>​context.state​</code>​ 暴露出來,根節點狀态則為 ​<code>​context.rootState​</code>​:

對于子產品内部的 getter,根節點狀态會作為第三個參數暴露出來:

2019.3.22更新:

使用action 來分發 (dispatch) 事件通知 store 去改變,這樣約定的好處是,我們能夠記錄所有 store 中發生的 state 改變,同時實作能做到記錄變更 (mutation)、儲存狀态快照、曆史復原/時光旅行的先進的調試工具。