快速了解Vuex
提要:提起react就會想起其應用最廣泛的redux狀態管理工具,vue中的官方推薦的狀態管理工具就是Vuex。
看到同事在鼓搗Vuex的東西,前面項目完成後也沒有好好總結一下Vuex的知識,所有就再回頭看看,溫故知新。
什麽是Vuex?
根據Vuex文檔中的描述,Vuex是使用於Vue.js應用的狀態管理庫,為應用中的所有組件提供集中式的狀態存儲與操作,保證了所有狀態以可預測的方式進行修改。
這個狀態自管理應用包含是三個部分:
- state: 驅動應用的數據源;
- view:以聲明方式將state映射到視圖;
- actions:相應view上的用戶輸入導致的狀態變化。
以下是一個表示“單項數據流”理念的極簡示意:
但是,當我們遇到多個組件共享狀態時,單向數據流的簡潔性就很容易被迫害:
多個視圖依賴於同一狀態。 來自不同視圖的行為需要變更同一種狀態。
對於問題一,傳參的方法對於多層嵌套的組件就會非常繁瑣,並且對於兄弟組件建的狀態傳遞無能為力。對於問題二,我們經常會采用父子組件直接引用或者時間來變更和同步狀態的多份拷貝。以上的這些模式非常脆弱,通常會導致無法代碼無法維護。
因此,我們可以把組件的狀態共享抽取出來,以一個全局單例模式管理。在這種模式下,我們的組件樹構成一個巨大的“視圖”,不管在樹的那個位置,任何組件都能夠獲取狀態或者出發行為。
另外,通過定義和隔離狀態管理中心的各種概念並強制遵循一定的規則,我們的代碼就會變得結構化且易維護。
這就是Vuex背後的基本思想,借鑒了Flux、Redux。所有以前用過Redux後,學習Vuex也沒有比較吃力的情況。
什麽情況下應該用Vuex
如果不需要開發大型單頁應用,使用Vuex可能是繁瑣冗余的。如果你的應用足夠簡單,最好不要使用Vuex。但是,如果需要構件一個大型單頁應用,就應該考慮使用組件外部的狀態管理工具,對於vue應用Vuex就成為自然而然的選擇。
Vuex中的幾個核心概念
學習一個知識點就需要掌握知識點中涉及到的一些核心概念,弄懂了概念,學習起來就能夠如魚得水。
State
單一狀態數
Vuex使用單一狀態樹,就是用一個對象就包含了全部的應用的層級狀態。所以它便作為一個【唯一數據源(SSOT)】而存在。這也意味著,每個應用將緊緊包含一個store實例。但一狀態樹讓我們能夠直接定位任一特定的狀態片段,在調試的過程中也能夠輕易地取得整個當前應用狀態的快照。
在Vue組件中獲取Vuex狀態
由於Vuex的狀態存儲是響應式的,從store實例中讀取狀態最賤單的方式就是在計算屬性中返回某個狀態:
// 創建一個 Counter 組件 const Counter = { template: `<div>{{ count }}</div>`, computed: { count () { return store.state.count } } }
沒當 store.state.count 變化的時候,就會從新求取計算屬性,並且觸發更新相關聯的DOM。
然而,這種模式導致組件依賴的全局狀態單例,在模塊化的構建系統中,在每個需要使用state的組件中需要頻繁地導入,並且在測試組件時需要模擬狀態。
Vuex通過store選項,提供了一種機制將狀態從根組件註入到每個子組件中(需要使用Vue.use(Vuex)):
const app = new Vue({ el: ‘#app‘, // 把 store 對象提供給 “store” 選項,這可以把 store 的實例註入所有的子組件 store, components: { Counter }, template: ` <div class="app"> <counter></counter> </div> ` })
通過在根事例中註冊store選項,該store實例會註入到根組件下的所有子組件中,且子組件能夠通過this.$store訪問到。
const Counter = { template: `<div>{{ count }}</div>`, computed: { count () { return this.$store.state.count } } }
mapState輔助函數
當一個組件需要獲取多個狀態時,將這些狀態都聲明為計算屬性會有些重復和冗余。為了解決這個問題,我們可以使用mapState輔助函數幫助我們生成計算屬性:
//在單獨構建的版本中輔助函數為Vuex.mapState import { mapState } from ‘vuex‘ export default { //... computed: mapState({ //箭頭函數可使代碼更簡練 count: state => state.count, //傳字符串‘count‘等同於`stete => state.count` countAlias: ‘count‘, //為了能夠使用`this`獲取局部狀態,必須使用常規函數 countPlusLocalState(state) { return state.count + this.localCount } }) }
當映射的計算屬性的名稱與state的子節點名稱相同時,我們也可以給mapState傳入一個字符串數組。
computed: mapState([ //映射 this.count 為 store.state.count ‘count‘ ])
對象展開運算符
mapState函數返回的是一個對象。我們如何將它與局部計算屬性混合使用呢?通常,我們需要使用一個工具函數來將多個對象合並為一個,以使我們可以將最終對象傳給computed屬性。但是自從有了對象展開運算符,現在我們可以極大地簡化寫法:
computed: { localComputed (){ }, //使用對象展開運算符將此對象混入到外部對象中 ...mapState({ //... }) }
組件仍然保有局部狀態
使用Vuex並不意味著需要將所有的狀態放入Vuex。雖然將所有的狀態放到Vuex會使狀態更顯式和易調試,但是會是代碼變得冗長和不直觀。如果有些狀態嚴格屬於單個組件,最好是作為組件的局部狀。應該能作為局部狀態的就保留局部狀態。
Getters
有時候我們需要從store中的state中派生出一些狀態,例如對列表盡心過濾並計數:
computed: { doneTodosCount () { return this.$store.state.todos.filter(todo => todo.done.length) } }
如果有多個組件需要用到此屬性,我們要麽復制這個函數,或者抽取到一個共享函數然後在多出導入它——無論哪種方式都不是很理想。
Vuex允許我們在store中定義“getters”(可以認為是store的計算屬性)。Getters接受state作為其第一個參數:
const store = new Vuex.Store({ state: { todos: [ {id: 1, text: "...", done: true}, {id: 2, text: "...", done: false} ] }, getters: { doneTodos: state => { return state.todos.filter(todo => todo.done) } } })
Getters會暴露出store.getters對象:
store.getters.doneTodos //-> [{id: 1, text: ‘...‘, done: true}]
Getters也可以接受其他getters作為第二個參數:
getters: { doneTodosCount: (state, getters)=> { return getters.doneTodos.length; } } store.getters.doneTodosCount //-> 1
我們可以很容易地在任何組件中使用它:
computed: { doneTodosCount(){ return this.$store.getters.doneTodosCount; } }
mapGetters輔助函數
mapGetters輔助函數僅僅是將store中的getters映射到局部計算屬性:
import {mapGetters} from ‘vuex‘ export default { computed: { // 使用對象展開運算符將 getters 混入 computed 對象中 ...mapGetters([ ‘doneTodosCount‘, ‘anotherGetter‘ ]) } }
如果你想講一個getter屬性另去一個名字,使用對象形式:
mapGetters({ // 映射 this.doneCount 為 store.getters.doneTodosCount doneCount: ‘doneTodosCount‘ })
未完
以上內容是看到同事在用mapGetters這個方法,突然想到前面做的Vue項目中並沒有去嘗試使用一些輔助函數去減少代碼量,於是乎回過頭來再看看Vuex的官網,把內容敲一遍,加深理解。後面項目中如果用到vue,就更加深入的研究一下Vue以及Vuex的一些彩蛋內容。
快速了解Vuex