Vuex是Vue中的一个状态管理插件,它采用集中式存储管理所有组件的公共状态, 并以相应的规则保证状态变化的一致性。

并不是所有情况都需要使用 Vuex, 通常是在一个界面中有多个组件同时操作一个数据时需要使用它。下面,我们通过一个例子来说明。

不使用 Vuex 的情况

我们定义了一个名为 TodoWidget 的组件,在组件中可以操作一个由外部(父组件)传入的值: title。 代码如下:

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
<template>
<div class="card">
<div>
<input v-model="title" placeholder="Please input new value"/>
</div>
<div>
title: {{ title }}
</div>
</div>

</template>

<script>
export default {
name: 'TodoWidget',
props: ['title']
}
</script>

<style>
.card {
border: 1px solid black;
margin: 10px 10px
}
</style>

现在,我们在项目的 App.vue 组件中使用这个控件,而且会加入两个 TodoWidget 的实例,修改后的 App.vue 代码如下:

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
<template>
<div id="app">
<div>
<h3>First</h3>
<todo-widget v-bind:title='title'></todo-widget>
</div>
<div>
<h3>Second</h3>
<todo-widget v-bind:title='title'></todo-widget>
</div>
<div>
<h1>Value</h1>
{{ title }}
</div>
</div>
</template>

<script>
import TodoWidget from './components/TodoWidget'

export default {
name: 'app',
components: {
TodoWidget
},
data() {
return {
title: ''
}
}
}
</script>

<style>

</style>

从代码中可以看到,我们在 App.vue 中放置了两个 TodoWidget 控件,并且传递了同一个值 title 给到这两个控件。我们期望的行为是改变其中一个的值,另一个也会自动改变。

但是,实际的运行效果并不是这样,两个组件实例中的 title 完全不能同步。

使用 Vuex

加入 vuex 插件

执行以下代码,在项目中加入 vuex

1
yarn add vuex

新增一个Store

  1. 加入一个 store.js 文件,在文件中定义一个 Store 并在其中加入状态 title, 代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use( Vuex );

const store = new Vuex.Store({
state: {
title: ''
},
mutations: {
update (state, ctitle) {
state.title = ctitle
}
}
})

export default store
  1. 修改 main.js 文件,将定义好的 store 加入到 Vue 对象中, 代码如下:
1
2
3
4
5
6
7
8
9
10
11
import Vue from 'vue'
import App from './App.vue'

import store from './store'

Vue.config.productionTip = false

new Vue({
store,
render: h => h(App),
}).$mount('#app')
  1. 修改 TodoWidget 的代码如下:
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
<template>
<div class="card">
<div>
<input v-model="ctitle"
placeholder="Please input new value" />
</div>
<div>
<button v-on:click="change">Change</button>
</div>
<div>
title: {{ this.$store.state.title }}
</div>
</div>

</template>

<script>
export default {
name: 'TodoWidget',
data() {
return {
ctitle: ''
}
},
methods: {
change: function() {
this.$store.commit('update', this.ctitle)
}
}
}
</script>

<style>
.card {
border: 1px solid black;
margin: 10px 10px
}
</style>
  1. 修改 App.vue 文件,代码如下:
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
<template>
<div id="app">
<div>
<h3>First</h3>
<todo-widget ></todo-widget>
</div>
<div>
<h3>Second</h3>
<todo-widget></todo-widget>
</div>
<div>
<h1>Value</h1>
{{ this.$store.state.title }}
</div>
</div>
</template>

<script>
import TodoWidget from './components/TodoWidget'

export default {
name: 'app',
components: {
TodoWidget
}
}
</script>

<style>

</style>

现在,可以在其中一个 TodoWidget 中输入新的 title值,然后按下 Change 按钮,可以看到状态被同步。