Vuex新手入門指南

昌維發表於2017-03-13

  很多人在學習完Vue.js之後還會看到一個經常被提及的詞語叫做Vuex。

  Vuex字面上看與Vue.js只差了一個字母,但是他們兩個做的事情完全不一樣。

  在本文我會像之前的 Vue.js新手入門指南 文章一樣的問答形式來寫文章。

 1.Vuex是什麼?

  我們還是像以往一樣先看一看官方文件對此的解讀(Vuex 是什麼? · GitBook

Vuex 是一個專為 Vue.js 應用程式開發的狀態管理模式。它採用集中式儲存管理應用的所有元件的狀態,並以相應的規則保證狀態以一種可預測的方式發生變化。Vuex 也整合到 Vue 的官方除錯工具 devtools extension,提供了諸如零配置的 time-travel 除錯、狀態快照匯入匯出等高階除錯功能。

  是不是又遇到了很多看起來很高大上聽起來卻一臉懵逼的專業術語?別急別急,我們慢慢來剖析一下這個Vuex究竟是個啥東西,他能做些啥。

 2.Vuex到底用來做什麼?

  用通俗一點的話來說,Vuex就是一個用於管理SPA專案(不知道SPA是什麼?請參考本專欄程式碼之美 - 知乎專欄中的歷史文章)中狀態的開源產品。

  接下來又引出了一個問題,什麼是狀態,為什麼要用Vuex這個東西去管理它?

 3.什麼是狀態?為什麼要去管理它?

  狀態這個東西其實我們生活中隨處可見。我們頭頂上的燈就有兩種狀態,一種是開,一種是關。狀態說白了就是燈這個物件的的某個屬性的值。

  如果你對狀態和屬性這兩個概念還是有所不瞭解,那麼我打一個其他的比方吧。

  我們平時是否有玩過王者榮耀或者英雄聯盟LOL之類的網遊?這些遊戲裡面每一個英雄當前都有生命值,法力值,攻擊力,法術強度,護甲和魔抗等等,這些是這個英雄的屬性,也就是英雄這個物件當前的狀態。

  屬性分為固定屬性和可變屬性,一般像LOL裡面大部分ADC英雄如果沒有特殊的被動或者其他裝備的支援,那麼它的的攻擊距離都是固定的,這個就是固定屬性,這種固定屬性的狀態由於正常情況下都是不變的,所以我們可以直接寫死在程式碼中(這種寫死在程式碼中的變數的值稱為硬編碼),但是像其他的攻擊力法術強度等等都是隨裝備和等級變化,那麼這種屬性是可變屬性。

  這些屬性的狀態由於會根據使用者的各種操作(比如說出裝備,打怪升級升級)變化。在傳統的Vue.js的元件化開發中,一般這些狀態都是分散在各個元件中,此時此刻如果兩個英雄互相打起來了,那麼就得分別去不同的元件中取狀態值,然後進行狀態值的修改,最後還要互相讀取對方的狀態值。如果他們本身是父子元件,那麼還可以通過事件觸發或者Prop屬性來傳遞狀態,但是如果是不同的元件,由於由於Vue.js本身元件之間有作用域,它們無法直接相互通訊,所以就需要一些東西例如Vuex去集中管理和追蹤它的變化。(如果你現在還是不明白這一大段話,可以好好回顧一下官方文件中元件 - vue.js非父子元件通訊 這個部分的內容)

  在遊戲中,這些狀態一般以變數的形式儲存在記憶體中,但是由於使用者玩遊戲的時候並不是直接去使用記憶體管理工具檢視他們在記憶體裡面的值,而是通過遊戲介面去看這些值,所以還需要像Vue.js這種MVVM框架將狀態同步到檢視中。這就是Vue.js和Vuex之間的關係了。

 4.什麼情況下我應該使用 Vuex?

  官方文件(Vuex 是什麼? · GitBook)中說:

雖然 Vuex 可以幫助我們管理共享狀態,但也附帶了更多的概念和框架。這需要對短期和長期效益進行權衡。

如果您不打算開發大型單頁應用,使用 Vuex 可能是繁瑣冗餘的。確實是如此——如果您的應用夠簡單,您最好不要使用 Vuex。一個簡單的 global event bus 就足夠您所需了。但是,如果您需要構建是一箇中大型單頁應用,您很可能會考慮如何更好地在元件外部管理狀態,Vuex 將會成為自然而然的選擇。引用 Redux 的作者 Dan Abramov 的話說就是:

Flux 架構就像眼鏡:您自會知道什麼時候需要它。

  他的意思其實就是如果開發的程式並不是很龐大,一個頁面中的元件不是很多並且他們之間並不需要大量頻繁的互相讀寫操作,那麼就可以直接使用傳統的Vue.js中的元件Prop或者事件觸發來修改狀態,或者用元件 - vue.js#非父子元件通訊 中介紹的new一個空的Vue物件例項,並且通過事件觸發等方式來跨元件通訊。

  否則的話還是建議使用Vuex。雖然Vuex本身需要有一段時間的學習成本,但是這個學習成本相對於你開發時期使用傳統非父子元件通訊機制遇到的各種坑來說還是比較划算的。這個就看你自己的權衡了。

 5.Vuex怎麼安裝和使用?

  在前面講解Vue.js入門的時候,我們用的是Vue-Cli這個腳手架工具來搭建的,由於這個腳手架工具本身會幫我們配置好npm的package.json檔案,這個檔案裡面包含了這個Vue.js專案中所有依賴的包。

  但是預設情況下這個腳手架工具沒有為我們將Vuex這個依賴包給包含進去,所以我們得自己去“宣告”一下我們這個Vue.js專案中需要依賴Vuex這個包。

  我們該怎麼“宣告”呢?現在有兩種辦法:

  一種是直接修改package.json,這種方法看起來有點複雜,很多新手怕一不小心修改出錯,可能會導致整個package.json檔案結構出錯,影響以後專案的依賴安裝。

  還有一種方法比較安全,只需要一行命令:

npm install vuex --save

  表示安裝vuex這個包,--save表示將這個依賴包與本專案的依賴關係寫入package.json中。

  然後我們僅僅安裝了這個依賴包是沒有用的,我們還得在之前Vue-Cli為我們自動構建好的專案檔案中的main.js主入口檔案的開頭裡面加上兩行這樣的程式碼:

import Vuex from 'vuex'
Vue.use(Vuex)

  第一行是用ECMAScript6的import將vuex包匯入進來(這個是不是和java中匯入jar包以及php中匯入名稱空間很相似?)

  第二行是Vue.js本身的外掛注入語法(參考官方文件外掛 - vue.js),將外掛注入Vue.js的目的是方便我們在外掛內部呼叫它。

 6.官方文件的五大核心概念是什麼?

  開啟官方文件(Introduction · GitBook)能看到五大核心概念,他們都是啥?看了半天官方文件我還是對它們沒什麼瞭解,樓主能不能以通俗易懂的方式講解一下它們的作用?當然可以啦!

  首先先看一遍這個程式碼,不需要你看懂它。

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

 7.State(狀態)

  官方文件:State · GitBook

  這個狀態就是前面所介紹的“狀態”值的存放處。

  看第6節末尾的程式碼中,狀態就是在state屬性中以鍵值對的形式宣告這個SPA中所有的狀態。上面的程式碼中宣告瞭一個count狀態。

  之所以要在這裡宣告所有狀態的原因,一是讓程式碼更加優雅,如果你接手你同事的專案的時候,能夠一眼從Vuex的狀態宣告中看出這個應用中有哪些狀態,肯定開發效率槓槓滴。二是如果在這裡宣告瞭狀態,那麼Vuex就能夠追蹤到這個狀態的變化。那麼Vue.js中就可以在檢視中對這個狀態做出響應。

  讀取狀態當然也是直接讀取這個屬性裡面的各種子屬性了。

 8.Getter(獲取器)

  官方文件:Getters · GitBook

  這個獲取器和一些後端開發中模型層ORM中的獲取器其實是差不多的功能。

  比如說後端返回給我們的是一個int型別的時間戳,我們想把這個時間戳轉換成正常人類可讀的文字型時間表現形式(比如說2017年3月11日 12:43:31),那麼我們就得在所有獲取該狀態的程式碼中增加一個轉換函式。

  可是現在有了狀態獲取器之後,我們可以統一將這個時間戳轉字串的函式寫在獲取器裡面,要呼叫的時候就直接呼叫獲取器就好了。

  還有一些其他場景也可以使用獲取器,比如說像錯誤碼這種東西一般都是一個數字碼對應一個文字形式的錯誤原因,我們也可以通過獲取器來實現通過錯誤碼拉取錯誤原因的功能。

  使用獲取器的方法則是直接呼叫Vuex例項的getter下的各種函式即可。

 9.Mutations(轉變)

  官方文件:Mutations · GitBook

  這個Mutations其實國內目前也沒有比較好的翻譯,通常我們都是直接稱Mutations。

  我們前面只講了可以通過呼叫Vuex的例項的state屬性或者getter獲取器來讀取狀態。但是沒講到如何修改狀態。

  官方文件中已經講了需要先在Vuex例項的Mutations下編寫對應的修改函式來修改狀態。並且要修改的時候,要通過Vuex例項的commit方法來提交修改。也就是說任何對state狀態的修改操作都必須寫在Mutations中,並且還得用Vuex例項的commit來提交修改操作,並且由於Mutations函式可以傳入引數,所以commit同理也可以傳入引數。

  這個時候可能有一些同學就會提問了,前面既然講到了讀取可以直接訪問Vuex例項的state屬性,為什麼修改卻不能直接去操作Vuex例項的state呢?官方文件和其他高手的回答是:

再次強調,我們通過提交 mutation 的方式,而非直接改變 store.state.count,是因為我們想要更明確地追蹤到狀態的變化。這個簡單的約定能夠讓你的意圖更加明顯,這樣你在閱讀程式碼的時候能更容易地解讀應用內部的狀態改變。此外,這樣也讓我們有機會去實現一些能記錄每次狀態改變,儲存狀態快照的除錯工具。有了它,我們甚至可以實現如時間穿梭般的除錯體驗。

  相當於我們通過一個Mutations函式可以顯式的在程式碼中告訴開發者,我們這個SPA中究竟會對狀態進行哪些操作,操作方式是什麼。並且在後期我們使用一些輔助開發工具,可以儲存狀態的快照,就像git或者svn一樣可以回滾狀態。如果你還是有點不明白,總之你就按照官方文件說的去做吧,等開發一段時間之後會慢慢明白作者的良苦用心的,哈哈。

  還有一個問題就是為什麼狀態修改的提交必須通過Vuex例項的commit方法提交呢?為什麼不能直接呼叫Mutations函式呢?除了上面官方文件中提到的原因,網上還有高手解釋了:因為Vue.js的狀態修改其實是在內部有一個修改佇列,通過commit的方式提交修改可以保證狀態的修改是有序的。

 10.Actions(動作)

  官方文件:Actions · GitBook

  前面提到了Mutations中可以對狀態進行操作,但是忘記告訴各位同學,Mutations中對狀態的操作只能是同步操作,不能是非同步操作。

  如果這個時候我們有一個對狀態的修改操作是非同步的怎麼辦呢?

  首先看看什麼是非同步操作?比如說ajax就可以選擇是否發起非同步請求,發起非同步請求之後,我們就需要在回撥函式裡面進行請求結果的處理。關於JavaScript非同步的知識大家可以先使用各種搜尋引擎自學一下。

  現在回到actions上來,看看官方文件(Actions · GitBook):

Action 類似於 mutation,不同在於:

  • Action 提交的是 mutation,而不是直接變更狀態。
  • Action 可以包含任意非同步操作。

  其實非同步的狀態修改本質上還是通過幾個同步操作組合的,所以我們還是得先宣告好mutation同步操作方法,然後在actions中進行非同步操作。如果我們暫時手頭上沒有ajax介面用於非同步請求,那麼我們可以像官方文件一樣用setTimeout這種最簡單的測試方法來理解。

actions: {
  incrementAsync ({ commit }) {
    setTimeout(() => {
      commit('increment')
    }, 1000)
  }
}

  前面講到了mutation是用commit來提交操作的,那麼actions是怎麼提交的呢?官方文件中說了actions是使用Vuex例項的dispatch方法來提交(其實說分發會更加準確)的。

  至於其他更詳細的actions操作官方文件講的還是比較清楚的,沒有什麼比較複雜的概念,可以參考官方文件學習,這裡不做更多講解。

  至於後面“組合actions”中提到的Promise物件以及 async / await 都是JavaScript中的一些特性,大家可以利用搜尋引擎進行更多瞭解。

 11.Modules(模組)

  官方文件:Modules · GitBook

  如果你的SPA專案非常的龐大,那麼狀態可能本身還需要進行分模組分類管理,這個時候就需要用到模組了。官方文件中已經給出了比較詳細的模組操作程式碼,這裡不再做更多講解。

  至於前面在將actions的時候,官方文件中說actions方法在宣告的時候需要帶上一個context引數,原因在這裡可以得到解答:

對於模組內部的 action,context.state 是區域性狀態,根節點的狀態是 context.rootState

 12.嚴格模式

  官方文件:嚴格模式 · GitBook

在嚴格模式下,無論何時發生了狀態變更且不是由 mutation 函式引起的,將會丟擲錯誤。這能保證所有的狀態變更都能被除錯工具跟蹤到。

  前面提到了state的修改需要通過提交Mutations或者分發Actions,但是事實上我直接修改state可以嗎?當然也是可以的,但是在開發階段,為了儘可能防止開發者直接修改,就可以通過嚴格模式來檢測這種錯誤的修改方式,並且丟擲異常。

  但是官方文件後面也提到了:

不要在釋出環境下啟用嚴格模式!嚴格模式會深度監測狀態樹來檢測不合規的狀態變更——請確保在釋出環境下關閉嚴格模式,以避免效能損失。

  因此不要在生產環境下開啟嚴格模式導致效能損失。

 結語:

  Vuex綜合來看是一個非常適合在Vue.js中使用的狀態管理工具,當然類似的狀態管理工具也有很多,比如說React的Redux。

  但是我們為了能夠儘可能保證專案穩定性以及學習曲線的平滑,推薦在Vue.js中使用Vuex。

  其實文章中也還有很多細節部分沒有講到,這些細節官方文件的說明還是比較通俗易懂這裡就不做更多搬運來湊字數。當然後面也有熱過載,測試等方面由於樓主自己的專案中也未使用過,所以不敢留有更多筆墨,還等樓主繼續探索實踐才能寫出更多好文章。

相關文章