Vue中的vuex介紹與使用(通俗版)

Vuex 是什麼?

Vuex 是一個專爲 Vue.js 應用程序開發的狀態管理模式,單一狀態樹,通俗理解就是一個應用的數據集合,可以想象爲一個“前端數據庫”(數據倉庫),讓其在各個頁面上實現數據的共享包括狀態,並且可操作。詳情官方解釋

Vuex的組成

Vuex分成五個部分:

  1. State:單一狀態樹
  2. Getters:狀態獲取
  3. Mutations:觸發同步事件
  4. Actions:提交mutation,可以包含異步操作
  5. Module:將vuex進行分模塊

前四個特徵就是核心。

正如官網vuex圖示,Vuex的數據總是“單向流動”

  • 用戶訪問頁面並觸發action
  • action提交mutation事件
  • mutation事件更改state狀態
  • state狀態改變後更新頁面(vue comptents)

Vuex 規定,屬於應用層級的狀態只能通過 Mutation 中的方法來修改,而派發 Mutation 中的事件只能通過 action。

用法

從左到右,從組件出發,組件中調用 action,在 action 這一層級我們可以和後臺數據交互,比如獲取初始化的數據源,或者中間數據的過濾等。然後在 action 中去派發 Mutation。Mutation 去觸發狀態的改變,狀態的改變,將觸發視圖的更新。

注意事項

  • 數據流都是單向的
  • 組件能夠調用 action
  • action 用來派發 Mutation
  • 只有 mutation 可以改變狀態
  • store 是響應式的,無論 state 什麼時候更新,組件都將同步更新

方式

方式一:可以將state,getter,mutation,action寫到一個store.js文件中可以參考
方式二:在src目錄下我們創一個vuex文件夾,分別創建index.js,mutations.js,state.js,getters.js,actions.js可以參考

這裏以方式一進行實例

State
在 store 中的 state 對象,可以理解爲 Vue 實例中的 data 對象,它用來保存最基本的數據。

import Vue from 'Vue';
import Vuex from 'Vuex';
Vue.use(Vuex);
let store = new Vuex.Store({
    state: {
        count: 0,
    }
});

如果希望Vuex狀態更新,相應的Vue組件也得到更新,最簡單的方法就是在Vue的computed(計算屬性)獲取state

// Counter 組件
const Counter = {
    template: `<div>{{ count }}</div>`,
    computed: {
        count () {
            return store.state.count;
        }
    }
}

上面的例子是直接操作全局狀態store.state.count,那麼每個使用該Vuex的組件都要引入。爲了解決這個,Vuex通過store選項,提供了一種機制將狀態從根組件注入到每一個子組件中。

// 根組件
import Vue from 'vue';
import Vuex from 'vuex';

Vue.use(Vuex);
const app = new Vue({
    el: '#app',
    store,
    components: {
        Counter
    },
    template: `
        <div class="app">
            <counter></counter>
        </div>
    `
})

通過這種注入機制,就能在子組件Counter通過this.$store訪問:

// Counter 組件
const Counter = {
    template: `<div>{{ count }}</div>`,
    computed: {
        count () {
            return this.$store.state.count
        }
    }
}

mapState函數
computed: {
    count () {
        return this.$store.state.count
    }
}

這樣通過count計算屬性獲取同名state.count屬性,是不是顯得太重複了,我們可以使用mapState函數簡化這個過程。

import { mapState } from 'vuex';

export default {
    computed: mapState ({
        count: state => state.count,
        countAlias: 'count',    // 別名 `count` 等價於 state => state.count
    })
}

還有更簡單的使用方法:

computed: mapState([
  // 映射 this.count 爲 store.state.count
  'count'
])

Getters
當需要對 store 中的數據進行處理,或者需要對處理後的數據在多個組件進行復用,就可以使用 Getters 來處理,Getters 也可以理解爲 Vue 中的計算屬性 (computed)。如下:

computed: {
    doneTodosCount () {
        return this.$store.state.todos.filter(todo => todo.done).length
    }
}

如果多個組件都要進行這樣的處理,那麼就要在多個組件中複製該函數。這樣是很沒有效率的事情,當這個處理過程更改了,還有在多個組件中進行同樣的更改,這就更加不易於維護。
Vuex中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)
    }
  }
})

在Vue中通過store.getters對象調用。

computed: {
  doneTodos () {
    return this.$store.getters.doneTodos
  }
}

Getter也可以接受其他getters作爲第二個參數:
getters: {
  doneTodos: state => {
      return state.todos.filter(todo => todo.done)
  },
  doneTodosCount: (state, getters) => {
    return getters.doneTodos.length
  }
}

mapGetters輔助函數
與mapState類似,都能達到簡化代碼的效果。mapGetters輔助函數僅僅是將store中的getters映射到局部計算屬性:

import { mapGetters } from 'vuex'

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

上面也可以寫作:

computed: mapGetters([
      'doneTodosCount',
      'anotherGetter',
      // ...
    ])

所以在Vue的computed計算屬性中會存在兩種輔助函數:

import { mapState, mapGetters } from 'vuex';

export default {
    // ...
    computed: {
        mapState({ ... }),
        mapGetter({ ... })
    }
}

Mutations
之前也說過了,更改Vuex的store中的狀態的唯一方法就是mutations。
每一個mutation都有一個事件類型type和一個回調函數handler。

const store = new Vuex.Store({
  state: {
    count: 1
  },
  mutations: {
    increment (state) {
      // 變更狀態
      state.count++
    }
  }
})

調用mutation,需要通過store.commit方法調用mutation type:

store.commit('increment')

Payload 提交載荷
也可以向store.commit傳入第二參數,也就是mutation的payload:

mutaion: {
    increment (state, n) {
        state.count += n;
    }
}

store.commit('increment', 10);

單單傳入一個n,可能並不能滿足我們的業務需要,這時候我們可以選擇傳入一個payload對象:

mutation: {
    increment (state, payload) {
        state.totalPrice += payload.price + payload.count;
    }
}

store.commit({
    type: 'increment',
    price: 10,
    count: 8
})

mapMutations函數
不例外,mutations也有映射函數mapMutations,幫助我們簡化代碼,使用mapMutations輔助函數將組件中的methods映射爲store.commit調用。

import { mapMutations } from 'vuex'

export default {
  // ...
  methods: {
    ...mapMutations([
      'increment' // 映射 this.increment() 爲 this.$store.commit('increment')
    ]),
    ...mapMutations({
      add: 'increment' // 映射 this.add() 爲 this.$store.commit('increment')
    })
  }
}

注 Mutations必須是同步函數。
如果我們需要異步操作,Mutations就不能滿足我們需求了,這時候我們就需要Actions了。

Actions
與 mutations 類似,不同模塊的 actions 均可以通過 store.dispatch 直接觸發。

const moduleA = {
    state: {
        count: 1
    },
    mutations: {
        sayCountA(state) {
            console.log('Module A count: ', state.count);
        }
    },
    actions: {
        maAction(context) {
            context.dispatch('mbAction');
        }
    }
};
const moduleB = {
    state: {
        count: 2
    },
    mutations: {
        sayCountB(state, num) {
            console.log('Module B count: ', state.count+num);
        }
    },
    action: {
        mbAction({ commit, rootState }) {
            commit('sayCountA');
            commit('sayCountB', rootState.a.count);
        }
    }
};
const store = {
    modules: {
        a: moduleA,
        b: moduleB
    }
};
store.dispatch('maAction'); // Module A count: 1、Module B count: 3

action 的回調函數接收一個 context 上下文參數,context 包含:1. state、2. rootState、3. getters、4. mutations、5. actions 五個屬性,

Module
Vuex 允許我們將 store 分割成模塊(module)。每個模塊擁有自己的 state、mutation、action、getter、甚至是嵌套子模塊——從上至下進行同樣方式的分割:

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 的狀態

場景

個人認爲vuex適合運用於一個視圖內組件與組件之間的組合傳遞,不適用於跨頁面,但是可以共用,爲了不因用戶刷新頁面,要進行初始化再次調用。在自己寫一些Ui組件給大家或者開源用的話,不適用於寫在vuex中,應該暴露所接收的Props,通過$emit來進行觸發事件,一些關鍵性全局狀態,不也適合存vuex中,你可以選擇localStorage,sessionStorage。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章