Vuex 是一個專爲 Vue.js 應用程序開發的狀態管理模式。它採用集中式存儲管理應用的所有組件的狀態,並以相應的規則保證狀態以一種可預測的方式發生變化。流程如下官網圖所示:
基礎知識
1. store
Vuex 的核心就是 store(倉庫),它就像一個容器,包含着應用中大部分的狀態 state .Vuex與全局對象的不同在於:
1)Vuex狀態存儲爲響應式的,store中的狀態發生改變時,相應的組件也會更新
2)不能直接改變store中的state,唯一途徑是顯示的提交(commit)mutation。這樣方便跟蹤每一個狀態的變化
2. 簡單的創建store
1)在main.js中引入創建store
* 創建後可以通過 store.state 來獲取狀態對象,以及通過 store.commit 方法觸發狀態變更
import Vue from 'vue'
import Vuex from 'vuex'
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
}
})
2) 然後在Vue實例中,提供創建好的store,如此便可在組件中訪問到this.$store中的property
new Vue({
el: '#app',
store: store,
})
3)現在可以在計算屬性computed中,返回狀態
computed:{
count(){
return this.$store.state.count;
}
}
4)當狀態需要改變時,可通過在組件的 methods 中提交(commit) mutation
methods: {
increment() {
this.$store.commit('increment')
console.log(this.$store.state.count)
}
}
3.四個核心概念
1)State
a)所有的狀態定義在state對象中;
b)從 store 實例中 讀取狀態 最簡單的方法就是在 計算屬性 中返回某個狀態;
c)Vuex提供一種機制將狀態從根組件“注入”到每一個子組件中( 需要調用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
}
}
}
d)輔助函數 mapState
當一個組件要獲取多個狀態的時候,可使用 mapState 輔助函數幫助我們生成計算屬性,這樣簡潔一些。
當映射的計算屬性的名稱與 state 的子節點名稱相同時,我們也可以給 mapState 傳一個字符串數組。
import { mapState, mapGetters } from 'vuex'
...
computed:{
...mapState(['userInfo']),
...mapGetters(['memberInfo'])
},
...
2)Getter
可以理解爲是獲取數據的,有時候我們需要從 store 中的 state 中派生出一些狀態,例如根據userState的值顯示用戶等級:
a)Getter 接受 state 作爲其第一個參數
export default{
memberInfo(state){
switch (state.userStatus){
case 0:
return '普通會員';
break;
case 1:
return 'VIP會員';
break;
case 2:
return '高級V'+ state.vipLevel +'會員';
break;
default:
return '普通會員';
break;
}
}
}
b)使用時,也可用輔助函數 mapGetters
3)Mutation
a)它是更改 Vuex 的 store 中狀態的唯一方法是提交 mutation. 而 mutation 類似一個事件,每一個 mutation 都有一個字符串的事件類型和一個回調函數,回調函數就是我們狀態要進行更改的地方,並且它會接受 state 作爲第一個參數。
b)Mutation 必須是同步函數
例如:登錄後修改userStatus
//登錄組件的login()中
//commit中有兩個參數
//第一個參數,對應的是mutation中的函數名
//第二個參數,多數情況下是一個對象
self.$store.commit('setMemberInfo',{
userStatus:0,
vipLevel:0
});
//mutations.js中
export default{
//登錄調用,重置用戶信息
login(state, v){
state.userInfo = v;
},
//重新修改用戶等級狀態
//第一個參數接收state
//第二個參數接收組件中commit傳來的值
setMemberInfo(state,v){
state.userStatus = v.userStatus;
state.vipLevel =v.vipLevel;
}
}
4)Action
Action 類似於 mutation,不同在於:
- Action 提交的是 mutation,而不是直接變更狀態。
- Action 可以包含任意異步操作。
a)註冊一個簡單的 action:
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
},
actions: {
increment (context) {
context.commit('increment')
}
}
})
由於 Action函數 接收一個 與store實例具有相同方法和屬性 的 context 對象,因此可以用context.commit來提交一個mutation,
而實踐中,我們會經常用到 ES2015 的 參數解構 來簡化代碼(特別是我們需要調用 commit
很多次的時候),如下案例:
actions: {
increment ({ commit }) {
commit('increment')
}
}
//actions.js中
export default{
//分發的時候有傳值的情況
buyVip({commit},e){
//mock api交互
return new Promise((resolve,reject)=>{
setTimeout(()=>{
//修改本地state
commit('setMemberInfo',{
userStatus:e.userStatus,
vipLevel:e.vipLevel
})
resolve('購買成功')
},1000)
})
},
//分發的時候無值,但需要使用state的情況
getFreeVip({commit,state}){
//mock api 交互
return new Promise(function(resolve,reject) {
setTimeout(function(){
if(state.userStatus === 0){
//僅限普通會員才能分享獲得高級會員資格
commit('setMemberInfo',{
userStatus:1,
vipLevel:0
});
resolve('分享成功,您已獲得一個月的高級vip');
}else{
resolve('分享成功');
}
},1000)
})
}
}
b)通過 dispatch 分發 action:
store.dispatch可以處理被觸發的action函數返回的Promise,並且仍舊返回Promise,如下:
methods:{
buy:function(e){
//正常是與後臺交互,傳遞會員信息與購買繳費
//此處是本地數據的處理
//第一個參數是actions.js中的函數名
//第二個參數是需要傳的值
store.dispatch('buyVip',e).then(res=>{
alert(res);
})
}
}