因爲狀態零散地分佈在許多組件和組件之間的交互中,大型應用複雜度也常常逐漸增加。爲了解決這個問題,Vue 提供 vuex。html
什麼是Vuex
Vuex 是一個專爲 Vue.js 應用程序開發的狀態管理模式。它採用集中式存儲管理應用的全部組件的狀態,並以相應的規則保證狀態以一種可預測的方式發生變化。vue
狀態,其實指的是實例之間的共享數據,Vuex是管理應用中不一樣Vue實例之間數據共享的工具。node
下圖是Vuex官方提供的對於狀態管理模式的設計理念。git
![](http://static.javashuo.com/static/loading.gif)
爲何使用Vuex
通俗的講,就是爲了方便組件與組件之間的數據共享。程序員
咱們知道,父子組件之間想要共享數據,可使用props屬性,子父組件之間的通訊可使用$emit觸發一個事件。github
隨着咱們的web應用不斷的豐富,組件以前通訊採用的props和事件將會充斥着咱們的整個項目,邏輯也會愈來愈複雜,程序員不得不將精力從業務邏輯的實現上轉移到組件之間的通訊上。往後的維護也會不斷的複雜。web
理論上,咱們能夠經過一個全局的共享數據來實現各個組件之間的數據共享,可是,當一個組件修改了這個全局數據時,其餘組件對該全局數據的引用都會跟隨者改變,而且不留下任何痕跡,這種實現方式,會致使調試變爲噩夢。ajax
這個時候,咱們迫切須要一個管理組件之間數據的共享的工具,Vuex出現了。vuex
什麼狀況下我該使用Vuex
雖然 Vuex 能夠幫助咱們管理共享狀態,但也附帶了更多的概念和框架。這須要對短時間和長期效益進行權衡。npm
若是您不打算開發大型單頁應用,使用 Vuex 多是繁瑣冗餘的。確實是如此——若是您的應用夠簡單,您最好不要使用 Vuex。一個簡單的 store 模式就足夠您所需了。可是,若是您須要構建一箇中大型單頁應用,您極可能會考慮如何更好地在組件外部管理狀態,Vuex 將會成爲天然而然的選擇。
如何使用Vuex
每個 Vuex 應用的核心就是 store(倉庫)。「store」基本上就是一個容器,它包含着你的應用中大部分的狀態 (state)。Vuex 和單純的全局對象有如下兩點不一樣:
- Vuex 的狀態存儲是響應式的。當 Vue 組件從 store 中讀取狀態的時候,若 store 中的狀態發生變化,那麼相應的組件也會相應地獲得高效更新。
- 你不能直接改變 store 中的狀態。改變 store 中的狀態的惟一途徑就是顯式地提交 (commit) mutation。這樣使得咱們能夠方便地跟蹤每個狀態的變化,從而讓咱們可以實現一些工具幫助咱們更好地瞭解咱們的應用。
安裝
本文使用npm安裝vuex,初始化一個vuex項目目錄,而後:
1
|
npm install vuex --save
|
也能夠直接下載vuex.js,而後在script標籤中引用。
開始使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
|
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="./node_modules/vue/dist/vue.min.js"></script> <script src="./node_modules/vuex/dist/vuex.min.js"></script> </head> <body> <div id="app"></div> <script>
Vue.use(Vuex);
const myStore = new Vuex.Store({ state: { name: "Alex", }, mutations: {}, getters: {}, actions: {} });
let App = { template: ` <div> <p>{{ name }}</p> </div> `, computed: { name: function () { return this.$store.state.name; }, } };
new Vue({ el: "#app", store: myStore, template: `<app></app>`, components: { 'app': App, }, }) </script>
</body> </html>
|
在以上代碼中,咱們使用Vue.use(Vuex)告訴Vue實例使用Vuex進行狀態管理,並使用Vuex.Store建立了一個Vuex實例,上文提到,Store是Vuex的核心,它是一個容器,包含了幾個核心屬性。打印一下Vue實例對象,咱們能夠看到這幾個核心屬性,以下圖:
![Vuex](http://static.javashuo.com/static/loading.gif)
建立Vue實例時將新建的Vuex實例註冊到Vue實例中,便可在Vue實例中查看到$store屬性。四個核心屬性都已經建立好了。
核心概念
接下來,咱們須要理解Vuex中的幾個核心概念。
State
用來存放組件之間共享的數據。他跟組件的data選項相似,只不過data選項是用來存放組件的私有數據。
Getter
有時候,咱們須要對state的數據進行篩選,過濾。這些操做都是在組件的計算屬性進行的。
若是多個組件須要用到篩選後的數據,那咱們就必須處處重複寫該計算屬性函數,或者將其提取到一個公共的工具函數中,並將公共函數多處導入 - 二者都不太理想。
若是把數據篩選完在傳到計算屬性裏就不用那麼麻煩了,這就是getter的做用,來看下面的代碼:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
|
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="./node_modules/vue/dist/vue.min.js"></script> <script src="./node_modules/vuex/dist/vuex.min.js"></script> </head> <body> <div id="app"></div>
<script> Vue.use(Vuex);
const store = new Vuex.Store({ state: { name: "Pizza", age: 22, }, getters: { getAge: function (state) { return state.age + 1; } } });
let App = { template: ` <div> <span>{{ name }}</span> <span>{{ age }}</span> </div> `, computed: { name: function () { return this.$store.state.name; }, age: function () { return this.$store.getters.getAge; } }, };
new Vue({ el: "#app", template: `<App></App>`, store: store, components: { App } }) </script>
</body> </html>
|
注意在getters中getAge必須使用state訪問數據,而不是this。
在計算屬性中,咱們使用this.$store.getters.getAge來訪問須要計算的數據。
Mutation
前面講到的都是如何獲取state的數據,那如何把數據存儲到state中呢?
在 Vuex store 中,實際改變狀態(state) 的惟一方式是經過提交(commit) 一個 mutation。
mutations下的函數接收state做爲第一個參數,接收payload做爲第二個參數,payload是用來記錄開發者使用該函數的一些信息,有一點須要注意:mutations方法必須是同步方法!
請看下面的代碼:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
|
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="./node_modules/vue/dist/vue.min.js"></script> <script src="./node_modules/vuex/dist/vuex.min.js"></script> </head> <body> <div id="app"></div>
<script> Vue.use(Vuex);
const store = new Vuex.Store({ state: { name: "Pizza", age: 18, score: 100, hobby: ["girls", "books"] }, mutations: { score: function (state, payload) { console.log("state: ", state); console.log("payload: ", payload); return state.score -= 10; }, hobby: function (state, payload) { return state.hobby.push(payload); } }, getters: { getAge: function (state) { return state.age + 1; } } });
let App = { template: ` <div> <p>{{ name }}</p> <p>{{ age }}</p> <p>{{ score }}</p> <p>{{ hobby }}</p> <button @click="changeScore">點擊修改分數</button> <button @click="changeHobby">點擊修改愛好</button> `, computed: { name: function () { return this.$store.state.name; }, age: function () { return this.$store.state.age; }, score: function () { return this.$store.state.score; }, hobby: function () { return this.$store.state.hobby; } }, methods: { changeScore: function () { this.$store.commit('score', 10); }, changeHobby: function () { this.$store.commit('hobby', 'movie') } } };
new Vue({ el: "#app", template: `<App></App>`, components: { App }, store }) </script>
</body> </html>
|
咱們提交了一個this.$store.commit(「score」, 10),傳入的第一個參數指的是mutations中定義的參數,第二個參數是須要變動的數據。
Action
Actions用來處理異步事務。
Actions 提交的是 mutations,而不是直接變動狀態。也就是說,actions會經過mutations,讓mutations幫他提交數據的變動。
Action 能夠包含任意異步操做。好比ajax、setTimeout、setInterval。繼續看下面的代碼:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
|
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="./node_modules/vue/dist/vue.min.js"></script> <script src="./node_modules/vuex/dist/vuex.min.js"></script> </head> <body> <div id="app"></div>
<script> Vue.use(Vuex);
const store = new Vuex.Store({ state: { name: 'Pizza', age: 18, score: 90, }, mutations: { changeScoreAsync: function (state, payload) { return state.score += payload; } }, actions: { addScore: function (context, payload) { setTimeout(() => context.commit("changeScoreAsync", payload), 3000); } } });
let App = { template: ` <div> <span>{{ name }}</span> <span>{{ age }}</span> <span>{{ score }}</span> <button @click="changeScore">點擊一下子修改數據</button> `, computed: { name: function () { return this.$store.state.name; }, age: function () { return this.$store.state.age; }, score: function () { return this.$store.state.score; }, }, methods: { changeScore: function () { this.$store.dispatch("addScore", 1); } } };
new Vue({ el: "#app", template: `<App></App>`, store, components: { App } }) </script>
</body> </html>
|
點擊修改數據後,this.$store.dispatch找到actions中定義的方法,並將當前狀態的store對象封裝,傳遞給actions中函數的第一個參數context,隨後由context.commit提交修改。
項目結構
Vuex 並不限制你的代碼結構。可是,它規定了一些須要遵照的規則:
- 應用層級的狀態應該集中到單個 store 對象中;
- 提交 mutation 是更改狀態的惟一方法,而且這個過程是同步的;
- 異步邏輯都應該封裝到 action 裏面。
只要你遵照以上規則,如何組織代碼隨你便。若是你的 store 文件太大,只需將 action、mutation 和 getter 分割到單獨的文件。
對於大型應用,咱們會但願把 Vuex 相關代碼分割到模塊中。下面是項目結構示例:
![vuex](http://static.javashuo.com/static/loading.gif)
以上,就是關於Vuex的使用介紹。