官網解釋以下:html
Vuex 是一個專爲 Vue.js 應用程序開發的狀態管理模式。它採用集中式存儲管理應用的全部組件的狀態,並以相應的規則保證狀態以一種可預測的方式發生變化。Vuex 也集成到 Vue 的官方調試工具 devtools extension,提供了諸如零配置的 time-travel 調試、狀態快照導入導出等高級調試功能。vue
那麼什麼是「狀態管理模式」呢?webpack
狀態自管理應用包括如下幾個部分:web
看下面這張表示「單向數據流」理念的簡單示意圖:ajax
從上面的圖中能夠看出:整個系統的數據流是單向的:數據(即state)去驅動視圖(view)的更新,用戶在view上面進行一些操做觸發action,經過action去更新state,而不是視圖直接更新state。vuex
下面來看一張官網上面的Vuex運行機制圖:npm
從圖中能夠看出,Vuex不在和組件強相關。運行機制:Vuex提供數據(state),來驅動視圖(這裏指的是Vue組件),視圖經過Dispatch派發Action,在Action中能夠進一步作一些異步的操做(例如經過ajax請求後端的接口數據),而後經過Commit提交給Mutations,由Mutations去最終更改state。那麼爲何要通過Mutations呢?這是由於咱們要在Vue調試工具(Devtools)中記錄數據的變化,這樣能夠經過插件去進行進一步的調試。因此說Mutations中只能是純同步的操做,若是是有異步操做,那麼就須要在Actions中進行處理。若是說沒有異步操做,那麼能夠直接由組件進行Commit操做Mutations。以下圖所示:後端
在命令行裏面輸入以下命令便可建立一個Vue項目:數組
vue init webpack vuex-demo
以下圖所示:瀏覽器
而後回車進行安裝,選擇默認配置便可,出現以下面所示的界面表示建立成功:
新建立的項目裏面默認不會安裝Vuex,若是要使用Vuex,須要進行手動安裝,輸入下面的命令:
npm install vuex --save
以下圖所示:
注:--save表示進行全局安裝
在第一步建立成功項目之後,最後會提示如何啓動項目,以下面所示:
這兩行命令表示先進入項目的根目錄,而後輸入npm run dev命令便可啓動項目,以下圖所示:
在瀏覽器窗口裏面輸入:http://localhost:8080進行瀏覽:
在src目錄下面建立一個store文件夾,該文件夾用來存放全部與狀態管理有關的文件,而後建立一個index.js文件,Store對象寫在index.js文件裏面,代碼以下:
// 引入Vue import Vue from 'vue' // 引入Vuex import Vuex from 'vuex' // 添加全局引用 Vue.use(Vuex); // 建立store對象 const store=new Vuex.Store({ }) // 導出建立的store對象 export default store;
在main.js裏面配置全局使用store對象:
// The Vue build version to load with the `import` command // (runtime-only or standalone) has been set in webpack.base.conf with an alias. import Vue from 'vue' import App from './App' import router from './router' // 引入index.js文件 import store from './store/index' Vue.config.productionTip = false /* eslint-disable no-new */ new Vue({ el: '#app', router, store,// 配置全局使用store對象 components: { App }, template: '<App/>' })
到此爲止,一個Vuex項目建立完成,而且安裝了Vuex,下面就可使用該項目演示如何使用Vuex進行狀態管理了。
在index.js文件裏面添加state,代碼以下:
// 引入Vue import Vue from 'vue' // 引入Vuex import Vuex from 'vuex' // 添加全局引用 Vue.use(Vuex); // 建立state const state={ // 定義num屬性並賦初始值爲1 num:1 } // 建立store對象 const store=new Vuex.Store({ // 添加state導出 state }) // 導出建立的store對象 export default store;
在test.vue中使用展現初始值:
<template> <p>初始值:{{this.$store.state.num}}</p> </template> <script> export default { name:'test' } </script>
頁面效果:
注:還能夠經過計算屬性獲取num的值:
<template> <div> <p>初始值:{{this.$store.state.num}}</p> <p>經過計算屬性獲取初始值:{{count}}</p> </div> </template> <script> export default { name:'test', // 計算屬性 computed:{ count(){ return this.$store.state.num; } } } </script>
效果如圖所示:
在上面使用了state能夠獲取到屬性的值,那麼如何修改state中屬性的值呢?這時候就要用到mutations了。官網解釋以下:
更改 Vuex 的 store 中的狀態的惟一方法是提交 mutation。Vuex 中的 mutation 很是相似於事件:每一個 mutation 都有一個字符串的 事件類型 (type) 和 一個 回調函數 (handler)。這個回調函數就是咱們實際進行狀態更改的地方,而且它會接受 state 做爲第一個參數。仍是以上面的爲例子。
修改index.js文件,添加mutations:
// 引入Vue
import Vue from 'vue'
// 引入Vuex
import Vuex from 'vuex'
// 添加全局引用
Vue.use(Vuex);
// 建立state
const state={
// 定義num屬性並賦初始值爲1
num:1
}
// 改變狀態,經過commit
var mutations={
// state做爲第一個參數
ChangeState(state){
// 自增1
state.num++;
}
}
// 建立store對象
const store=new Vuex.Store({
// 添加state
state,
// 導出mutations
mutations
})
// 導出建立的store對象
export default store;
修改test.vue文件:
<template> <div> <p>初始值:{{this.$store.state.num}}</p> <p>經過計算屬性獲取初始值:{{count}}</p> <button @click="$store.commit('ChangeState')">改變屬性值</button> </div> </template> <script> export default { name:'test', // 計算屬性 computed:{ count(){ return this.$store.state.num; } } } </script>
效果:
注:改變狀態還能夠傳遞參數進行修改,看下面的例子:
修改index.js文件以下:
// 引入Vue
import Vue from 'vue'
// 引入Vuex
import Vuex from 'vuex'
// 添加全局引用
Vue.use(Vuex);
// 建立state
const state={
// 定義num屬性並賦初始值爲1
num:1
}
// 改變狀態,經過commit
var mutations={
// state做爲第一個參數
ChangeState(state){
// 自增1
state.num++;
},
// state做爲第一個參數,para做爲第二個參數
ChangeStateWithPara(state,para){
// 自增1
state.num += para;
}
}
// 建立store對象
const store=new Vuex.Store({
// 添加state
state,
// 導出mutations
mutations
})
// 導出建立的store對象
export default store;
test.vue修改以下:
<template> <div> <p>初始值:{{this.$store.state.num}}</p> <p>經過計算屬性獲取初始值:{{count}}</p> <button @click="$store.commit('ChangeState')">改變屬性值</button> <button @click="$store.commit('ChangeStateWithPara',5)">傳遞參數改變屬性值</button> </div> </template> <script> export default { name:'test', // 計算屬性 computed:{ count(){ return this.$store.state.num; } } } </script>
效果:
有時候咱們須要從store中的state中派生出一些狀態,例如根據num的值返回基數或者偶數。若是有多個組件須要用到這個狀態,那麼咱們就須要在每一個組件裏面都定義重複的一個函數,或者是定義成一個公共的函數,而後在多個組件裏面導入,這兩種方式不管採用哪一種都不是很理想的。這時候咱們就須要用到getter了。官網解釋以下:
Vuex 容許咱們在 store 中定義「getter」(能夠認爲是 store 的計算屬性)。就像計算屬性同樣,getter 的返回值會根據它的依賴被緩存起來,且只有當它的依賴值發生了改變纔會被從新計算。一樣,Getter接受state做爲第一個參數。看下面的例子:
修改index.js文件以下:
// 引入Vue
import Vue from 'vue'
// 引入Vuex
import Vuex from 'vuex'
// 添加全局引用
Vue.use(Vuex);
// 建立state
const state={
// 定義num屬性並賦初始值爲1
num:1
}
// 改變狀態,經過commit
var mutations={
// state做爲第一個參數
ChangeState(state){
// 自增1
state.num++;
},
// state做爲第一個參數,para做爲第二個參數
ChangeStateWithPara(state,para){
// 自增1
state.num += para;
}
}
// Getter,至關於store裏面的計算屬性
var getters={
IsOddOrEven(state){
return state.num % 2==0?'偶數':'奇數'
}
}
// 建立store對象
const store=new Vuex.Store({
// 添加state
state,
// 導出mutations
mutations,
// 導出getter
getters
})
// 導出建立的store對象
export default store;
那麼該如何訪問getters呢?能夠經過下面的幾種方式進行訪問:
修改test.vue以下:
<template> <div> <p>初始值:{{this.$store.state.num}}</p> <p>經過計算屬性獲取初始值:{{count}}</p> <button @click="$store.commit('ChangeState')">改變屬性值</button> <button @click="$store.commit('ChangeStateWithPara',5)">傳遞參數改變屬性值</button> <!--經過屬性訪問getters--> <p>經過屬性訪問getters:{{$store.getters.IsOddOrEven}}</p> </div> </template> <script> export default { name:'test', // 計算屬性 computed:{ count(){ return this.$store.state.num; } } } </script>
效果:
vuex中提供了mapGetters輔助函數,mapGetters輔助函數僅僅是將store中的getter映射到局部計算屬性:
修改text.vue以下:
<template> <div> <p>初始值:{{this.$store.state.num}}</p> <p>經過計算屬性獲取初始值:{{count}}</p> <button @click="$store.commit('ChangeState')">改變屬性值</button> <button @click="$store.commit('ChangeStateWithPara',5)">傳遞參數改變屬性值</button> <!--經過屬性訪問getters--> <p>經過屬性訪問getters:{{$store.getters.IsOddOrEven}}</p> <!--經過輔助函數訪問getters--> <p>經過輔助函數訪問getters:{{IsOddOrEven}}</p> </div> </template> <script> // 導入輔助函數 import {mapGetters} from 'vuex' export default { name:'test', // 計算屬性 computed:{ count(){ return this.$store.state.num; }, // 使用對象展開運算符將 getter 混入 computed 對象中,數組裏面是在getters裏面定義的方法名稱,若是有多個 // 則在數組裏面添加多個便可 ...mapGetters(['IsOddOrEven']) } } </script>
效果:
能夠看出:經過屬性訪問和經過輔助函數訪問實現的效果是同樣的。
Vuex官網對Action的解釋以下:
Action相似於Mutation,二者的不一樣之處在於:
看下面的例子:
修改index.js以下:
// 引入Vue import Vue from 'vue' // 引入Vuex import Vuex from 'vuex' // 添加全局引用 Vue.use(Vuex); // 建立state const state={ // 定義num屬性並賦初始值爲1 num:1 } // 改變狀態,經過commit var mutations={ // state做爲第一個參數 ChangeState(state){ // 自增1 state.num++; }, // state做爲第一個參數,para做爲第二個參數 ChangeStateWithPara(state,para){ // 自增1 state.num += para; } } // Getter,至關於store裏面的計算屬性 var getters={ IsOddOrEven(state){ return state.num % 2==0?'偶數':'奇數' } } // 用來管理mutations var actions={ ExecChangeState({commit}){ // 執行mutations裏面定義的ChangeState方法 commit('ChangeState'); } } // 建立store對象 const store=new Vuex.Store({ // 添加state state, // 導出mutations mutations, // 導出getter getters, // 導出actions actions }) // 導出建立的store對象 export default store;
修改test.vue以下:
<template> <div> <p>初始值:{{this.$store.state.num}}</p> <p>經過計算屬性獲取初始值:{{count}}</p> <button @click="$store.commit('ChangeState')">改變屬性值</button> <button @click="$store.commit('ChangeStateWithPara',5)">傳遞參數改變屬性值</button> <!--經過屬性訪問getters--> <p>經過屬性訪問getters:{{$store.getters.IsOddOrEven}}</p> <!--經過輔助函數訪問getters--> <p>經過輔助函數訪問getters:{{IsOddOrEven}}</p> <!--演示Action--> <div> <p>屬性值:{{count}}</p> <button @click="add">經過action改變屬性值</button> </div> </div> </template> <script> // 導入輔助函數 import {mapGetters} from 'vuex' export default { name:'test', // 計算屬性 computed:{ count(){ return this.$store.state.num; }, // 使用對象展開運算符將 getter 混入 computed 對象中,數組裏面是在getters裏面定義的方法名稱,若是有多個 // 則在數組裏面添加多個便可 ...mapGetters(['IsOddOrEven']) }, methods:{ add(){ // 經過dispatch觸發actions裏面的ExecChangeState this.$store.dispatch('ExecChangeState'); } } } </script>
效果:
下面看一下執行順序:
分別在add()、ExecChangeState()、ChangeState()裏面添加一句console.log()來查看執行順序:
index.js修改以下:
// 引入Vue import Vue from 'vue' // 引入Vuex import Vuex from 'vuex' // 添加全局引用 Vue.use(Vuex); // 建立state const state={ // 定義num屬性並賦初始值爲1 num:1 } // 改變狀態,經過commit var mutations={ // state做爲第一個參數 ChangeState(state){ console.log("mutations"); // 自增1 state.num++; }, // state做爲第一個參數,para做爲第二個參數 ChangeStateWithPara(state,para){ // 自增1 state.num += para; } } // Getter,至關於store裏面的計算屬性 var getters={ IsOddOrEven(state){ return state.num % 2==0?'偶數':'奇數' } } // 用來管理mutations var actions={ ExecChangeState({commit}){ // 執行mutations裏面定義的ChangeState方法 console.log("actions"); commit('ChangeState'); } } // 建立store對象 const store=new Vuex.Store({ // 添加state state, // 導出mutations mutations, // 導出getter getters, // 導出actions actions }) // 導出建立的store對象 export default store;
test.vue修改以下:
<template> <div> <p>初始值:{{this.$store.state.num}}</p> <p>經過計算屬性獲取初始值:{{count}}</p> <button @click="$store.commit('ChangeState')">改變屬性值</button> <button @click="$store.commit('ChangeStateWithPara',5)">傳遞參數改變屬性值</button> <!--經過屬性訪問getters--> <p>經過屬性訪問getters:{{$store.getters.IsOddOrEven}}</p> <!--經過輔助函數訪問getters--> <p>經過輔助函數訪問getters:{{IsOddOrEven}}</p> <!--演示Action--> <div> <p>屬性值:{{count}}</p> <button @click="add">經過action改變屬性值</button> </div> </div> </template> <script> // 導入輔助函數 import {mapGetters} from 'vuex' export default { name:'test', // 計算屬性 computed:{ count(){ return this.$store.state.num; }, // 使用對象展開運算符將 getter 混入 computed 對象中,數組裏面是在getters裏面定義的方法名稱,若是有多個 // 則在數組裏面添加多個便可 ...mapGetters(['IsOddOrEven']) }, methods:{ add(){ // 經過dispatch觸發actions裏面的ExecChangeState console.log("觸發add()"); this.$store.dispatch('ExecChangeState'); } } } </script>
效果:
從上圖能夠看出執行順序是:先執行組件中的方法,而後執行actions,最後在執行mutations,最終經過mutations更新狀態。
除了使用dispatch()觸發actions之外,還可使用輔助函數mapActions,輔助函數mapActions將組件中的methods映射爲store.dispatch。修改test.vue以下:
<template> <div> <p>初始值:{{this.$store.state.num}}</p> <p>經過計算屬性獲取初始值:{{count}}</p> <button @click="$store.commit('ChangeState')">改變屬性值</button> <button @click="$store.commit('ChangeStateWithPara',5)">傳遞參數改變屬性值</button> <!--經過屬性訪問getters--> <p>經過屬性訪問getters:{{$store.getters.IsOddOrEven}}</p> <!--經過輔助函數訪問getters--> <p>經過輔助函數訪問getters:{{IsOddOrEven}}</p> <!--演示Action--> <div> <p>屬性值:{{count}}</p> <button @click="add">經過action改變屬性值</button> </div> <!--經過輔助函數觸發actions--> <div> <p>屬性值:{{count}}</p> <button @click="ExecChangeState">經過輔助函數改變屬性值</button> </div> </div> </template> <script> // 導入輔助函數 import {mapGetters, mapActions} from 'vuex' export default { name:'test', // 計算屬性 computed:{ count(){ return this.$store.state.num; }, // 使用對象展開運算符將 getter 混入 computed 對象中,數組裏面是在getters裏面定義的方法名稱,若是有多個 // 則在數組裏面添加多個便可 ...mapGetters(['IsOddOrEven']) }, methods:{ add(){ // 經過dispatch觸發actions裏面的ExecChangeState console.log("觸發add()"); this.$store.dispatch('ExecChangeState'); }, // 輔助函數 mapActions輔助函數將組件中的methods映射爲store.dispatch // 這裏表示將'this.ExecChangeState'映射爲'this.$store.dispatch('ExecChangeState')' ...mapActions(['ExecChangeState']) } } </script>
效果:
到了這裏,可能有人會問:經過actions的方式進行管理mutations比直接使用mutations更復雜了,爲何還要這麼作呢?實際上並不是如此,mutations只能執行同步方法,Action就不受此限制,咱們能夠在actions內部執行異步方法,看下面的例子:
修改index.js,添加異步方法,代碼以下:
// 引入Vue import Vue from 'vue' // 引入Vuex import Vuex from 'vuex' // 添加全局引用 Vue.use(Vuex); // 建立state const state={ // 定義num屬性並賦初始值爲1 num:1 } // 改變狀態,經過commit var mutations={ // state做爲第一個參數 ChangeState(state){ console.log("mutations"); // 自增1 state.num++; }, // state做爲第一個參數,para做爲第二個參數 ChangeStateWithPara(state,para){ // 自增1 state.num += para; } } // Getter,至關於store裏面的計算屬性 var getters={ IsOddOrEven(state){ return state.num % 2==0?'偶數':'奇數' } } // 用來管理mutations var actions={ ExecChangeState({commit}){ // 執行mutations裏面定義的ChangeState方法 console.log("actions"); commit('ChangeState'); }, // 執行異步方法:點擊按鈕5秒以後再改變屬性的值 ExecChangeStateAsync({commit}){ setTimeout(() => { commit('ChangeState') }, 5000); } } // 建立store對象 const store=new Vuex.Store({ // 添加state state, // 導出mutations mutations, // 導出getter getters, // 導出actions actions }) // 導出建立的store對象 export default store;
修改test.vue文件:
<template> <div> <p>初始值:{{this.$store.state.num}}</p> <p>經過計算屬性獲取初始值:{{count}}</p> <button @click="$store.commit('ChangeState')">改變屬性值</button> <button @click="$store.commit('ChangeStateWithPara',5)">傳遞參數改變屬性值</button> <!--經過屬性訪問getters--> <p>經過屬性訪問getters:{{$store.getters.IsOddOrEven}}</p> <!--經過輔助函數訪問getters--> <p>經過輔助函數訪問getters:{{IsOddOrEven}}</p> <!--演示Action--> <div> <p>屬性值:{{count}}</p> <button @click="add">經過action改變屬性值</button> </div> <!--經過輔助函數觸發actions--> <div> <p>屬性值:{{count}}</p> <button @click="ExecChangeState">經過輔助函數改變屬性值</button> </div> <!--執行異步actions--> <div> <p>屬性值:{{count}}</p> <button @click="ExecChangeStateAsync">經過異步方法改變屬性值</button> </div> </div> </template> <script> // 導入輔助函數 import {mapGetters, mapActions} from 'vuex' export default { name:'test', // 計算屬性 computed:{ count(){ return this.$store.state.num; }, // 使用對象展開運算符將 getter 混入 computed 對象中,數組裏面是在getters裏面定義的方法名稱,若是有多個 // 則在數組裏面添加多個便可 ...mapGetters(['IsOddOrEven']) }, methods:{ add(){ // 經過dispatch觸發actions裏面的ExecChangeState console.log("觸發add()"); this.$store.dispatch('ExecChangeState'); }, // 輔助函數 mapActions輔助函數將組件中的methods映射爲store.dispatch // 這裏表示將'this.ExecChangeState'映射爲'this.$store.dispatch('ExecChangeState')' // 這裏表示將'this.ExecChangeStateAsync'映射爲'this.$store.dispatch('ExecChangeStateAsync')' ...mapActions(['ExecChangeState','ExecChangeStateAsync']) } } </script>
效果:
因爲使用單一狀態樹,應用的全部狀態會集中到一個比較大的對象。當應用變得很是複雜時,store對象就有可能變得至關臃腫。爲了解決上面的問題,Vuex容許咱們將store分割成模塊(Module)。每一個模塊都有本身的state、mutation、action、getter。
經過上面的基本講解,瞭解了Vuex的一些基本用法,Vuex在進行狀態管理方面很方便,但並非說Vue項目中就必定要使用Vuex,在Vuex中也能夠不使用Vuex。下面總結一下Vuex中的核心概念和底層原理