1. 程式人生 > 實用技巧 >vue-狀態管理vuex

vue-狀態管理vuex

一、認識Vuex

1.1 Vuex是做什麼的?

  • 官方解釋:Vuex 是一個專為 Vue.js 應用程式開發的狀態管理模式。
    • 它採用 集中式儲存管理 應用的所有元件的狀態,並以相應的規則保證狀態以一種可預測的方式發生變化。
    • Vuex 也整合到 Vue 的官方除錯工具 devtools extension,提供了諸如零配置的 time-travel 除錯、狀態快照匯入匯出等高階除錯功能。
  • 狀態管理到底是什麼?
    • 狀態管理模式、集中式儲存管理這些名詞聽起來就非常高大上,讓人捉摸不透。
    • 其實,你可以簡單的將其看成把需要多個元件共享的變數全部儲存在一個物件裡面。
    • 然後,將這個物件放在頂層的Vue例項中,讓其他元件可以使用。
    • 那麼,多個元件是不是就可以共享這個物件中的所有變數屬性了呢?
  • 等等,如果是這樣的話,為什麼官方還要專門出一個外掛Vuex呢?難道我們不能自己封裝一個物件來管理嗎?
    • 當然可以,只是我們要先想想VueJS帶給我們最大的便利是什麼呢?沒錯,就是響應式。
    • 如果你自己封裝實現一個物件能不能保證它裡面所有的屬性做到響應式呢?當然也可以,只是自己封裝可能稍微麻煩一些。
    • 不用懷疑,Vuex就是為了提供這樣一個在多個元件間共享狀態的外掛,用它就可以了。

1.2.管理什麼狀態呢?

  • 但是,有什麼狀態時需要我們在多個元件間共享的呢?
    • 如果你做過大型開放,你一定遇到過多個狀態,在多個介面間的共享問題。
    • 比如使用者的登入狀態、使用者名稱稱、頭像、地理位置資訊等等。
    • 比如商品的收藏、購物車中的物品等等。
    • 這些狀態資訊,我們都可以放在統一的地方,對它進行儲存和管理,而且它們還是響應式的(待會兒我們就可以看到程式碼了,莫著急)。
  • OK,從理論上理解了狀態管理之後,讓我們從實際的程式碼再來看看狀態管理。
    • 畢竟,Talk is cheap, Show me the code.(來自Linus)
  • 我們先來看看但介面的狀態管理吧.

1.3.單介面的狀態管理

  • 我們知道,要在單個元件中進行狀態管理是一件非常簡單的事情
    • 什麼意思呢?我們來看下面的圖片。
  • 這圖片中的三種東西,怎麼理解呢?
    • State:不用多說,就是我們的狀態。(你姑且可以當做就是data中的屬性)
    • View:檢視層,可以針對State的變化,顯示不同的資訊。(這個好理解吧?)
    • Actions:這裡的Actions主要是使用者的各種操作:點選、輸入等等,會導致狀態的改變。
  • 寫點程式碼,加深理解:
    • 看下右邊的程式碼效果, 肯定會實現吧?

1.4.單介面狀態管理的實現

  • 在這個案例中,我們有木有狀態需要管理呢?沒錯,就是個數counter。
  • counter需要某種方式被記錄下來,也就是我們的State。
  • counter目前的值需要被顯示在介面中,也就是我們的View部分。
  • 介面發生某些操作時(我們這裡是使用者的點選,也可以是使用者的input),需要去更新狀態,也就是
  • 我們的Actions
  • 這不就是上面的流程圖了嗎?

1.5.多介面狀態管理

  • Vue已經幫我們做好了單個介面的狀態管理,但是如果是多個介面呢?
    • 多個試圖都依賴同一個狀態(一個狀態改了,多個介面需要進行更新)
    • 不同介面的Actions都想修改同一個狀態(Home.vue需要修改,Profile.vue也需要修改這個狀態)
  • 也就是說對於某些狀態(狀態1/狀態2/狀態3)來說只屬於我們某一個試圖,但是也有一些狀態(狀態a/
    • 狀態b/狀態c)屬於多個試圖共同想要維護的
    • 狀態1/狀態2/狀態3你放在自己的房間中,你自己管理自己用,沒問題。
    • 但是狀態a/狀態b/狀態c我們希望交給一個大管家來統一幫助我們管理!!!
    • 沒錯,Vuex就是為我們提供這個大管家的工具。
  • 全域性單例模式(大管家)
    • 我們現在要做的就是將共享的狀態抽取出來,交給我們的大管家,統一進行管理。
    • 之後,你們每個試圖,按照我規定好的規定,進行訪問和修改等操作。
    • 這就是Vuex背後的基本思想。

1.6.Vuex狀態管理圖例

  • 一起在來看一副官方給出的圖片

二、Vuex 基本使用

npm install Vuex --save

2.1.簡單的案例

  • 我們還是實現一下之前簡單的案例

  • 首先,我們需要在某個地方存放我們的Vuex程式碼:

    • 這裡,我們先建立一個資料夾store,並且在其中建立一個index.js檔案
    • 在index.js檔案中寫入如下程式碼:

2.2.掛載到Vue例項中

  • 其次,我們讓所有的Vue元件都可以使用這個store物件
    • 來到main.js檔案,匯入store物件,並且放在new Vue中
    • 這樣,在其他Vue元件中,我們就可以通過this.$store的方式,獲取到這個store物件了

2.3.使用Vuex的count

  • 好的,這就是使用Vuex最簡單的方式了。
  • 我們來對使用步驟,做一個簡單的小節:
    • 1.提取出一個公共的store物件,用於儲存在多個元件中共享的狀態
    • 2.將store物件放置在new Vue物件中,這樣可以保證在所有的元件中都可以使用到
    • 3.在其他元件中使用store物件中儲存的狀態即可
      • 通過this.$store.state.屬性的方式來訪問狀態
      • 通過this.$store.commit('mutation中方法')來修改狀態
  • 注意事項:
    • 我們通過提交mutation的方式,而非直接改變store.state.count。
    • 這是因為Vuex可以更明確的追蹤狀態的變化,所以不要直接改變store.state.count的值。

三、Vuex核心概念

  • Vuex有幾個比較核心的概念:
    • State
    • Getters
    • Mutation
    • Action
    • Module
  • 我們對它進行一一介紹.

3.1.State單一狀態樹

  • Vuex提出使用單一狀態樹, 什麼是單一狀態樹呢?
    • 英文名稱是Single Source of Truth,也可以翻譯成單一資料來源。
  • 但是,它是什麼呢?我們來看一個生活中的例子。
    • OK,我用一個生活中的例子做一個簡單的類比。
    • 我們知道,在國內我們有很多的資訊需要被記錄,比如上學時的個人檔案,工作後的社保記錄,公積金記錄,結婚後的婚姻資訊,以及其他相關的戶口、醫療、文憑、房產記錄等等(還有很多資訊)。
    • 這些資訊被分散在很多地方進行管理,有一天你需要辦某個業務時(比如入戶某個城市),你會發現你需要到各個對應的工作地點去列印、蓋章各種資料資訊,最後到一個地方提交證明你的資訊無誤。
    • 這種儲存資訊的方案,不僅僅低效,而且不方便管理,以及日後的維護也是一個龐大的工作(需要大量的各個部門的人力來維護,當然國家目前已經在完善我們的這個系統了)。
  • 這個和我們在應用開發中比較類似:
    • 如果你的狀態資訊是儲存到多個Store物件中的,那麼之後的管理和維護等等都會變得特別困難。
    • 所以Vuex也使用了單一狀態樹來管理應用層級的全部狀態。
    • 單一狀態樹能夠讓我們最直接的方式找到某個狀態的片段,而且在之後的維護和除錯過程中,也可以非常方便的管理和維護

3.2.Getters基本使用

  • 有時候,我們需要從store中獲取一些state變異後的狀態,比如下面的Store中:

    • 獲取學生年齡大於20的個數。
  • 我們可以在Store中定義getters

3.3.Getters作為引數和傳遞引數

  • 如果我們已經有了一個獲取所有年齡大於20歲學生列表的getters, 那麼程式碼可以這樣來寫

  • getters預設是不能傳遞引數的, 如果希望傳遞引數, 那麼只能讓getters本身返回另一個函式.

    • 比如上面的案例中,我們希望根據ID獲取使用者的資訊

3.4.Mutation狀態更新

  • Vuex的store狀態的更新唯一方式:提交Mutation

  • Mutation主要包括兩部分:

    • 字串的事件型別(type)
    • 一個回撥函式(handler),該回調函式的第一個引數就是state。
  • mutation的定義方式:

  • 通過mutation更新

3.5.Mutation傳遞引數

  • 在通過mutation更新資料的時候, 有可能我們希望攜帶一些額外的引數

    • 引數被稱為是mutation的載荷(Payload)
  • Mutation中的程式碼:

  • 但是如果引數不是一個呢?

    • 比如我們有很多引數需要傳遞.
    • 這個時候, 我們通常會以物件的形式傳遞, 也就是payload是一個物件.
    • 這個時候可以再從物件中取出相關的資訊.

3.6.Mutation提交風格

  • 上面的通過commit進行提交是一種普通的方式

  • Vue還提供了另外一種風格, 它是一個包含type屬性的物件

  • Mutation中的處理方式是將整個commit的物件作為payload使用, 所以程式碼沒有改變, 依然如下:

3.7.Mutation響應規則

  • Vuex的store中的state是響應式的, 當state中的資料發生改變時, Vue元件會自動更新.

  • 這就要求我們必須遵守一些Vuex對應的規則:

    • 提前在store中初始化好所需的屬性.
    • 當給state中的物件新增新屬性時, 使用下面的方式:
      • 方式一: 使用Vue.set(obj, 'newProp', 123)
      • 方式二: 用心物件給舊物件重新賦值
  • 我們來看一個例子:

    • 當我們點選更新資訊時, 介面並沒有發生對應改變.
  • 如何才能讓它改變呢?

    • 檢視下面程式碼的方式一和方式二
    • 都可以讓state中的屬性是響應式的.

3.8.Mutation常量型別 – 概念

  • 我們來考慮下面的問題:
    • 在mutation中, 我們定義了很多事件型別(也就是其中的方法名稱).
    • 當我們的專案增大時, Vuex管理的狀態越來越多, 需要更新狀態的情況越來越多, 那麼意味著Mutation中的方法越來越多.
    • 方法過多, 使用者需要花費大量的經歷去記住這些方法, 甚至是多個檔案間來回切換, 檢視方法名稱, 甚至如果不是複製的時候, 可能還會出現寫錯的情況.
  • 如何避免上述的問題呢?
    • 在各種Flux實現中, 一種很常見的方案就是使用常量替代Mutation事件的型別.
    • 我們可以將這些常量放在一個單獨的檔案中, 方便管理以及讓整個app所有的事件型別一目瞭然.
  • 具體怎麼做呢?
    • 我們可以建立一個檔案: mutation-types.js, 並且在其中定義我們的常量.
    • 定義常量時, 我們可以使用ES2015中的風格, 使用一個常量來作為函式的名稱.

3.8.1.Mutation常量型別 – 程式碼

3.9.Mutation同步函式

  • 通常情況下, Vuex要求我們Mutation中的方法必須是同步方法.

    • 主要的原因是當我們使用devtools時, 可以devtools可以幫助我們捕捉mutation的快照.
    • 但是如果是非同步操作, 那麼devtools將不能很好的追蹤這個操作什麼時候會被完成.
  • 比如我們之前的程式碼, 當執行更新時, devtools中會有如下資訊: 圖1

  • 但是, 如果Vuex中的程式碼, 我們使用了非同步函式: 圖2

  • 你會發現state中的info資料一直沒有被改變, 因為他無法追蹤到.

  • So, 通常情況下, 不要再mutation中進行非同步的操作

四、 Action

4.1. Action的基本定義

  • 我們強調, 不要再Mutation中進行非同步操作.
    • 但是某些情況, 我們確實希望在Vuex中進行一些非同步操作, 比如網路請求, 必然是非同步的. 這個時候怎麼處理呢?
    • Action類似於Mutation, 但是是用來代替Mutation進行非同步操作的.
  • Action的基本使用程式碼如下:
  • context是什麼?
    • context是和store物件具有相同方法和屬性的物件.
    • 也就是說, 我們可以通過context去進行commit相關的操作, 也可以獲取context.state等.
    • 但是注意, 這裡它們並不是同一個物件, 為什麼呢? 我們後面學習Modules的時候, 再具體說.
  • 這樣的程式碼是否多此一舉呢?
    • 我們定義了actions, 然後又在actions中去進行commit, 這不是脫褲放屁嗎?
    • 事實上並不是這樣, 如果在Vuex中有非同步操作, 那麼我們就可以在actions中完成了.

4.2.Action的分發

  • 在Vue元件中, 如果我們呼叫action中的方法, 那麼就需要使用dispatch

  • 同樣的, 也是支援傳遞payload

4.3.Action返回的Promise

  • 前面我們學習ES6語法的時候說過, Promise經常用於非同步操作.

    • 在Action中, 我們可以將非同步操作放在一個Promise中, 並且在成功或者失敗後, 呼叫對應的resolve或reject.
  • OK, 我們來看下面的程式碼:

五、Module

5.1.認識Module

  • Module是模組的意思, 為什麼在Vuex中我們要使用模組呢?
    • Vue使用單一狀態樹,那麼也意味著很多狀態都會交給Vuex來管理.
    • 當應用變得非常複雜時,store物件就有可能變得相當臃腫.
    • 為了解決這個問題, Vuex允許我們將store分割成模組(Module), 而每個模組擁有自己的state、mutations、actions、getters等
  • 我們按照什麼樣的方式來組織模組呢?
    • 我們來看程式碼

5.2.Module區域性狀態‘

  • 上面的程式碼中, 我們已經有了整體的組織結構, 下面我們來看看具體的區域性模組中的程式碼如何書寫.

    • 我們在moduleA中新增state、mutations、getters
    • mutation和getters接收的第一個引數是區域性狀態物件
  • 注意:

    • 雖然, 我們的doubleCount和increment都是定義在物件內部的.
    • 但是在呼叫的時候, 依然是通過this.$store來直接呼叫的.

六、Actions的寫法

  • actions的寫法呢? 接收一個context引數物件

    • 區域性狀態通過 context.state 暴露出來,根節點狀態則為 context.rootState
  • 如果getters中也需要使用全域性的狀態, 可以接受更多的引數

八、專案結構組織

8.1.專案結構

  • 當我們的Vuex幫助我們管理過多的內容時, 好的專案結構可以讓我們的程式碼更加清晰.