vuex 深入理解


參考自:https://mp.weixin.qq.com/s?src=11&timestamp=1528275978&ver=922&signature=ZeHPZ2ZrLir4L71RaART3yZCuTpJQg6gf9uyq2a2nRSHchg97bQH2C3s*qtYsPC7fHNgmtkW3B0NIV4LYUnOgI5-f*yBPY-baXwcfW6i6m*kqB26UDs0MKq3UMH71rOK&new=1

1、問題

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

2、vuex

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

vuex關鍵詞:

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

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

3、集中式狀態管理模式

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

  • 定義組件自身的初始數據

  • 在組件內獲取異步數據

  • 根據數據渲染更新視圖

但事情並非那么完美,由於這種方式封裝的組件的內部實現聚合了異步請求的數據和自身的狀態,真正組裝復用起來是存在一定問題的。比如:

  • 在同一可視區域的冗余請求數

  • 不同層級組件的數據共享問題

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

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

不能直接修改 Vuex 的狀態:修改 Vuex 的狀態唯一途徑是提交(commit) mutations 來實現修改。

 

 

  • 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

<a href="javascript:;" @click="$store.state.show = true">點擊</a>

嚴格模式下,無論何時發生了狀態變更且不是由 mutation 函數引起的,將會拋出錯誤

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

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

actions: { switch_dialog (context) { // 這里的context和我們使用的$store擁有相同的對象和方法
      context.commit('switch_dialog') // 你還可以在這里觸發其他的mutations方法
 },

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

<template>
  <el-dialog :visible.sync="show"></el-dialog>
</template>

<script> import {mapState} from 'vuex' export default { computed:{ //這里的三點叫做 : 擴展運算符
 ...mapState({ show: state => state.dialog.show }), } } </script>

 mapState 函數返回的是一個對象

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

(6)、mapGetters 輔助函數僅僅是將 store 中的 getter 映射到局部計算屬性:

import { mapGetters } from 'vuex' export default { // ...
 computed: { // 使用對象展開運算符將 getter 混入 computed 對象中
 ...mapGetters([ 'doneTodosCount', 'anotherGetter', // ...
 ]) } }

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

// mutation-types.js
export const SOME_MUTATION = 'SOME_MUTATION'
// store.js
import Vuex from 'vuex' import { SOME_MUTATION } from './mutation-types' const store = new Vuex.Store({ state: { ... }, mutations: { // 我們可以使用 ES2015 風格的計算屬性命名功能來使用一個常量作為函數名
 [SOME_MUTATION] (state) { // mutate state
 } } })

(8)mapMutations 

你可以在組件中使用 this.$store.commit('xxx') 提交 mutation,或者使用 mapMutations 輔助函數將組件中的 methods 映射為 store.commit 調用(需要在根節點注入 store)。

import { mapMutations } from 'vuex' export default { // ...
 methods: { ...mapMutations([ 'increment', // 將 `this.increment()` 映射為 `this.$store.commit('increment')`

      // `mapMutations` 也支持載荷:
      'incrementBy' // 將 `this.incrementBy(amount)` 映射為 `this.$store.commit('incrementBy', amount)`
 ]), ...mapMutations({ add: 'increment' // 將 `this.add()` 映射為 `this.$store.commit('increment')`
 }) } }

(9)Action 

Action 函數接受一個與 store 實例具有相同方法和屬性的 context 對象,因此你可以調用 context.commit提交一個 mutation,或者通過 context.state 和 context.getters 來獲取 state 和 getters。當我們在之后介紹到 Modules 時,你就知道 context 對象為什么不是 store 實例本身了。

寫法一:

const store = new Vuex.Store({ state: { count: 0 }, mutations: { increment (state) { state.count++ } }, actions: { increment (context) { context.commit('increment') } } })

參數解構寫法:

actions: { increment ({ commit }) { commit('increment') } }

(10)mapActions 

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

import { mapActions } from 'vuex' export default { // ...
 methods: { ...mapActions([ 'increment', // 將 `this.increment()` 映射為 `this.$store.dispatch('increment')`

      // `mapActions` 也支持載荷:
      'incrementBy' // 將 `this.incrementBy(amount)` 映射為 `this.$store.dispatch('incrementBy', amount)`
 ]), ...mapActions({ add: 'increment' // 將 `this.add()` 映射為 `this.$store.dispatch('increment')`
 }) } }

(11)module

const moduleA = { state: { ... }, mutations: { ... }, actions: { ... }, getters: { ... } } const moduleB = { state: { ... }, mutations: { ... }, actions: { ... } } const store = new Vuex.Store({ modules: { a: moduleA, b: moduleB } }) store.state.a // -> moduleA 的狀態
store.state.b // -> moduleB 的狀態
# 模塊的局部狀態

對於模塊內部的 mutation 和 getter,接收的第一個參數是模塊的局部狀態對象。

const moduleA = { state: { count: 0 }, mutations: { increment (state) { // 這里的 `state` 對象是模塊的局部狀態
      state.count++ } }, getters: { doubleCount (state) { return state.count * 2 } } }

同樣,對於模塊內部的 action,局部狀態通過 context.state 暴露出來,根節點狀態則為 context.rootState

const moduleA = { // ...
 actions: { incrementIfOddOnRootSum ({ state, commit, rootState }) { if ((state.count + rootState.count) % 2 === 1) { commit('increment') } } } }

對於模塊內部的 getter,根節點狀態會作為第三個參數暴露出來:

const moduleA = { // ...
 getters: { sumWithRootCount (state, getters, rootState) { return state.count + rootState.count } } }

 

 

2019.3.22更新:

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM