vue學習六之vuex

因爲狀態零散地分佈在許多組件和組件之間的交互中,大型應用複雜度也常常逐漸增加。爲了解決這個問題,Vue 提供 vuex。html

什麼是Vuex

Vuex 是一個專爲 Vue.js 應用程序開發的狀態管理模式。它採用集中式存儲管理應用的全部組件的狀態,並以相應的規則保證狀態以一種可預測的方式發生變化。vue

狀態,其實指的是實例之間的共享數據,Vuex是管理應用中不一樣Vue實例之間數據共享的工具。node

下圖是Vuex官方提供的對於狀態管理模式的設計理念。git

爲何使用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

建立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

以上,就是關於Vuex的使用介紹。

相關文章
相關標籤/搜索