Vue学习-Vuex

前言

本文将介绍Vuex的用法。


Vuex

介绍

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。(官方解释)

  • 它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
  • Vuex也集成到Vue的官方调试工具devtoolsextension,提供了诸如零配置的time-travel调试、状态快照导入导出等高级调试功能。

简单理解:不同的组件需要访问一些同一状态量,如果把这些状态量随意定义到任意组件中都不合适,我们希望能够有一个对象对这些共享状态量进行统一封装,而Vuex就是这样一个提供在多个组件间共享状态的插件。

举例一些共享状态量:用户的登陆状态、用户名称、头像、地理位置信息等。

安装

打开终端,进入项目目录,键入如下命令(注意版本号):

1
npm install vuex --save

基本使用

共享状态量会统一放一起管理,在项目的src文件夹下新建一个名为store的文件夹,在其中新建index.js文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import Vue from 'vue'
import Vuex from 'vuex'

// 1.安装插件
Vue.use(Vuex)

// 2.新建Vuex.Store对象
const store = new Vuex.Store({
state: {
counter: 1
},
mutations:{},
actions:{},
getters:{},
modules:{}
})

// 3.导出store独享
export default store

这里在store对象中的state属性中定义了一个counter(之后用于其他组件的访问)

注意:

  • Vuex专门提供了一个Store类,初始化应创建store对象
  • Store对象中有五种常用属性:statemutationsactionsgettersmodules(之后会详细介绍)

接着在项目的main.js文件中导入并注册store组件:

1
2
3
4
5
6
7
8
9
10
11
import Vue from 'vue'
import App from './App'
import store from './store' //导入

Vue.config.productionTip = false

new Vue({
el: '#app',
store, //注册
render: h => h(App)
})

一切准备完成之后就可以在组件中直接访问定义的共享状态量counter了,这里以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
<template>
<div id="app">
<h2>我是App标题</h2>
<h2>{{$store.state.counter}}</h2>
<button @click="$store.state.counter++">+</button>
<button @click="$store.state.counter--">-</button>
</div>
</template>

<script>

export default {
name: 'App',
data() {
return {
message: '我是App页面内容'
}
},
}
</script>

<style>

</style>

说明:

可以直接通过$store.state.xxx的形式访问,但是一般不会直接访问并随意修改其状态。

Devtools

Vuex的状态管理

Vue官方提供了一个Vuex状态管理图例:

当然可以直接在组件中修改状态量,但是官方并不建议这么做。因为在最初介绍的时候提到过一些调试工具,如接下来要讲的devtools,当多个组件实例对同一状态量进行修改时,它可以对该状态量进行跟踪,方便调试。如果跨过Mutations直接对状态量进行修改,则调试工具就不能够跟踪。

安装Devtools

在浏览器的扩展中查找Vue.js devtools插件:

可以看到提供方就是Vue官方。

下载完成之后就可以看到在有Vue项目页面的情况下,开发者工具中就会多出一个Vue的选项。

注意:安装完插件后需要重新启动浏览器才可以看到。

利用该插件就可以对Vue项目中的内容做一些跟踪查看:

mutations用法

正常想要改变状态量的值需要通过mutations(Store对象中的一个属性),可以在其中定义方法:

index.js:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

const store = new Vuex.Store({
state: {
counter: 1
},
mutations:{
increase(state) { //自动传入state对象
state.counter++
},
decrease(state) { //自动传入state对象
state.counter--
}
},
actions:{},
getters:{},
modules:{}
})

export default store

说明:

mutations属性中可以定义方法,其中传入的参数就是state对象,通过在其中定义方法去修改状态量。

接着就是在组件中调用定义的方法:

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
<template>
<div id="app">
<h2>我是App标题</h2>
<h2>{{$store.state.counter}}</h2>
<button @click="add">+</button> <!-- +按钮绑定add方法 -->
<button @click="reduce">-</button> <!-- -按钮绑定reduce方法 -->
</div>
</template>

<script>

export default {
name: 'App',
methods: {
add() { //定义add方法
this.$store.commit('increase') //调用mutations中的increase方法
},
reduce() { //定义reduce方法
this.$store.commit('decrease') //调用mutations中的decrease方法
}
}
}
</script>

<style>

</style>

说明:

实例中需要再在methods属性中定义对应的方法去调用mutations中的方法,语法如下:

$store.commit('方法名')

最后就可以使用Devtools调试工具跟踪状态量的变化情况。

Getters

getters是Store对对象中的五个属性之一。

基本使用

getters的一个用法类似于组件中的computed计算属性。

例如,要多次获取state属性中的counter值乘100,不必在每次调用时都去计算,而是在getters中定义一个计算属性:

index.js:

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
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

const store = new Vuex.Store({
state: {
counter: 1
},
mutations:{
increase(state) {
state.counter++
},
decrease(state) {
state.counter--
}
},
actions:{},
getters:{
mul(state) { //自动传入state对象
return state.counter*100 //定义计算属性mul
}
},
modules:{}
})

export default store

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
<template>
<div id="app">
<h2>我是App标题</h2>
<h2>{{$store.state.counter}}</h2>
<button @click="add">+</button>
<button @click="reduce">-</button>
<h2>获取mul的值:{{$store.getters.mul}}</h2> <!--获取store中getters属性的mul计算属性-->
</div>
</template>

<script>

export default {
name: 'App',
methods: {
add() {
this.$store.commit('increase')
},
reduce() {
this.$store.commit('decrease')
}
}
}
</script>

<style>

</style>

说明:

调用语法:$store.getters.xxx

参数传递

getters中也可以使用一些高阶函数来实现一些功能。

以过滤器filter为例,现在在state属性中定义了students数组,里面存放了一些学生信息,希望通过getter实现一些信息过滤(获取年龄大于20岁的学生信息):

index.js:

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
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

const store = new Vuex.Store({
state: {
students: [
{id: 110, name: '张三', age: 18},
{id: 111, name: '李四', age: 24},
{id: 112, name: '王五', age: 30},
{id: 113, name: '赵六', age: 10}
],
},
mutations:{},
actions:{},
getters:{
studentsShow(state) {
return state.students.filter(s => s.age > 20) //返回年龄大于20的学生信息
}
},
modules:{}
})

export default store

App.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<template>
<div id="app">
<h2>我是App标题</h2>
<h2>获取学生信息:{{$store.getters.studentsShow}}</h2> <!--获取store中getters属性的studentsShow信息-->
</div>
</template>

<script>

export default {
name: 'App',
}
</script>

<style>

</style>

如果想要获取年龄大于20岁的学生人数,可以如下操作:

index.js

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
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

const store = new Vuex.Store({
state: {
students: [
{id: 110, name: '张三', age: 18},
{id: 111, name: '李四', age: 24},
{id: 112, name: '王五', age: 30},
{id: 113, name: '赵六', age: 10}
],
},
mutations:{},
actions:{},
getters:{
studentsShow(state) {
return state.students.filter(s => s.age > 20) //返回年龄大于20的学生信息
},
studentsNumber(state,getters) {
// return state.students.filter(s => s.age > 20).length //方式一:利用state属性
return getters.studentsShow.length //方式二:直接利用getters属性
}
},
modules:{}
})

export default store

说明:

  • 方式一:仍使用state属性,但是返回值书写过于繁杂
  • 方式二:可以在参数中传递getters属性,利用studentsShow获取列表长度

App.vue

1
2
3
4
5
6
7
<template>
<div id="app">
<h2>我是App标题</h2>
<h2>获取学生信息:{{$store.getters.studentsShow}}</h2> <!--获取store中getters属性的studentsShow信息-->
<h2>获取学生人数:{{$store.getters.studentsNumber}}</h2> <!--获取store中getters属性的studentsNumber信息-->
</div>
</template>

如果学生过滤的条件想让前端用户输入而不是在index.js中写死,那么getters中定义的计算属性可以返回函数:

index.js

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
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

const store = new Vuex.Store({
state: {
students: [
{id: 110, name: '张三', age: 18},
{id: 111, name: '李四', age: 24},
{id: 112, name: '王五', age: 30},
{id: 113, name: '赵六', age: 10}
],
},
mutations:{},
actions:{},
getters:{
studentsShow(state) {
return function(age) {
return state.students.filter(s => s.age > age) //返回年龄大于age的学生信息
}
},
},
modules:{}
})

export default store

还可以使用箭头函数简写:

1
2
3
4
5
studentsShow(state) {
return age => {
return state.students.filter(s => s.age > age) //返回年龄大于age的学生信息
}
},

可以在App.vue中动态接收参数:

1
2
3
4
5
6
<template>
<div id="app">
<h2>我是App标题</h2>
<h2>获取学生信息:{{$store.getters.studentsShow(25)}}</h2> <!--studentsShow返回一个函数,这里可以传参-->
</div>
</template>

Mutations

基本使用

前面也提到过Mutations是为了更新store的状态(且官方也指出这是Vuex更新状态量的唯一方式)。

Mutation主要包括两部分:

  • 字符串的事件类型(type):可以理解为就是函数的定义名称。这在组件中使用commit方法时会用到:

    $store.commit('事件类型')

  • 回调函数(handler):该回调函数的第一个参数就是state

参数传递

参数唯一

在通过mutation更新数据的时候,有时希望附带一些额外的参数。

仍然以计数器案例展示效果,现在希望在Vue模板中仅调用一个方法就实现不同的计数按钮:

首先就需要在index.js文件中的mutation内定义该方法(含参):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

const store = new Vuex.Store({
state: {
counter: 1
},
mutations:{
increase(state, num) { //第二个参数num即为Vue对象中定义的methods方法中传递来的number
state.counter += num
},
decrease(state, num) { //第二个参数num即为Vue对象中定义的methods方法中传递来的number
state.counter -= num
},
},
actions:{},
getters:{},
modules:{}
})

export default store

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
<template>
<div id="app">
<h2>我是App标题</h2>
<h2>{{$store.state.counter}}</h2>
<button @click="add(5)">+5</button> <!--调用含参方法-->
<button @click="reduce(5)">-5</button> <!--调用含参方法-->
</div>
</template>

<script>

export default {
name: 'App',
methods: {
add(number) { //接收前端传来的参数
this.$store.commit('increase',number) //将参数传递给Vuex对象中的mutations内定义的方法
},
reduce(number) { //接收前端传来的参数
this.$store.commit('decrease',number) //将参数传递给Vuex对象中的mutations内定义的方法
}
}
}
</script>

<style>

</style>

说明:

仅依次案例展示mutation的参数传递,该参数可以为其他类型(如:对象)。

payload(多个参数)

实际上通过mutation更新状态量时,被传递的参数称为mutation的载荷(Payload,是一个对象)。在传递参数的数量仅为一个的时候,可以用上面的方法。如果要传递多个参数,建议使用Payload。

案例:在上一个计数器的基础上多增加一个描述信息传递:

index.js

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
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

const store = new Vuex.Store({
state: {
counter: 1
},
mutations:{
increase(state, payload) {
state.counter += payload.number
console.log(payload.desc)
},
decrease(state, payload) {
state.counter -= payload.number
console.log(payload.desc)
},
},
actions:{},
getters:{},
modules:{}
})

export default store

说明:

Vue对象传递过来的对象类型的参数用payload接收,在使用的时候也从payload中调用多个参数。

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
<template>
<div id="app">
<h2>我是App标题</h2>
<h2>{{$store.state.counter}}</h2>
<button @click="add(5)">+5</button> <!--调用含参方法-->
<button @click="reduce(5)">-5</button> <!--调用含参方法-->
</div>
</template>

<script>

export default {
name: 'App',
methods: {
add(number) { //接收前端传来的参数
this.$store.commit({
type: 'increase', //指明提交的事件类型
number, //参数一
desc: '执行计数器加法' //参数二
})
},
reduce(number) { //接收前端传来的参数
this.$store.commit({
type: 'decrease', //指明提交的事件类型
number, //参数一
desc: '执行计数器减法' //参数二
})
}
}
}
</script>

<style>

</style>

说明:

commit的参数为一个对象,其中包含有type用于指明提交的事件类型,以及其他参数。

响应式规则

注意:vue4版本已更新该规则。

如果是在state中已经定义并且初始化的数据,则这些数据都是响应式的(即对数据做修改后会在页面实时显示),但是未在state中定义的数据,并且想要在方法中进行增添或删除的操作,则该操作就是非响应式的(虽然数据被已修改,但是并不会在页面实时显示)。

以学生信息增添为例:

定义在state的学生对象中共有idnameage三种属性,如果想要增添一个address的信息,有可能会出现非响应式的情况:

index.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

const store = new Vuex.Store({
state: {
studetInfo: {
id: 110,
name: '张三',
age: 18}
},
mutations:{
updateInfo(state) {
state.studetInfo['address'] = '北京'
}
},
actions:{},
getters:{},
modules:{}
})

export default store

说明:

这里使用的更新数据的方式为:state.studetInfo['address'] = '北京'

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
<template>
<div id="app">
<h2>我是App标题</h2>
<h2>{{$store.state.studetInfo}}</h2>
<button @click="updateInfo">更新</button>
</div>
</template>

<script>

export default {
name: 'App',
methods: {
updateInfo() {
return this.$store.commit('updateInfo')
}
}
}
</script>

<style>

</style>

效果如下:

可以看到右侧确实显示有添加了address的信息,但是左侧页面并无显示。

如果想要做到响应式,则需要使用Vue.set方法:

1
2
3
updateInfo(state) {
Vue.set(state.studetInfo, 'address', '北京')
}

Vue.set参数说明:

Vue.set(状态量名, 属性名, 属性值)

如果要删除的话,类似的可以使用Vue.delete方法:

Vue.delete(状态量名, 属性名)

例如删除学生age属性:

1
2
3
updateInfo(state) {
Vue.delete(state.studetInfo, 'age')
}

常量类型

在修改状态量时规定需要经过mutations,但是在实际开发中会定义许多事件类型(方法名称),相应的在vue对象中也需要提交不同的commit参数。不便于后期的管理,也容易出错。因此官方给出了一种方案:使用常量代替Mutations中的事件类型。

(仍以计数器案例展示)

首先在store文件夹中创建mutations-types.js,在其中定义常量:

1
2
export const INCREASE = 'increase'
export const DECREASE = 'decrease'

然后在index.js中导入并使用这些常量:

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
import Vue from 'vue'
import Vuex from 'vuex'
import * as types from './mutations-types' //导入方式一:*全部直接导入

Vue.use(Vuex)

const store = new Vuex.Store({
state: {
counter: 1
},
mutations:{
[types.INCREASE](state) { //使用INCREASE常量(注意格式)
state.counter++
},
[types.DECREASE](state) { //使用DECREASE常量(注意格式)
state.counter--
}
},
actions:{},
getters:{
},
modules:{}
})

export default store

注意:

此时的事件类型需要用中括号包裹常量使用。

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
<template>
<div id="app">
<h2>我是App标题</h2>
<h2>{{$store.state.counter}}</h2>
<button @click="add">+</button>
<button @click="reduce">-</button>
</div>
</template>

<script>
import {INCREASE,DECREASE} from './store/mutations-types' //导入方式二:按需导入

export default {
name: 'App',
methods: {
add() {
this.$store.commit(INCREASE) //使用INCREASE常量
},
reduce() {
this.$store.commit(DECREASE) //使用DECREASE常量
}
}
}
</script>

<style>

</style>

Actions

Mutations同步说明

Vuex要求Mutation中更新状态量的方法必须是同步方法,如果在其中实现异步方法,那么Devtools检测工具就会失效,无法跟踪状态量的变化。

下面仍以setTimeout()代替实现异步方法,做一个信息打印案例:

index.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

const store = new Vuex.Store({
state: {
message: '原始信息'
},
mutations:{
showMessage(state) {
setTimeout(() => {
state.message = '更新信息'
},1000)
}
},
actions:{},
getters:{
},
modules:{}
})

export default store

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
<template>
<div id="app">
<h2>我是App标题</h2>
<h2>{{$store.state.message}}</h2>
<button @click="update">更新</button>
</div>
</template>

<script>

export default {
name: 'App',
methods: {
update() {
return this.$store.commit('showMessage')
}
}
}
</script>

<style>

</style>

效果如下:

可以看到页面信息进行了更新,但是检测工具并未捕捉到状态量的变化。(实际状态量确实发生了改变)

说明:插件已开启自动更新状态量设置。

官方规定如果要定义异步方法(请求),需要经过Actions。

基本使用

操作

说明:

Actions仍然可以像Mutations一样在方法名处用常量类型调用方法,使用格式不变。

(出于演示方便,以下的代码中并未使用常量类型的形式)

Actions类似于Mutations,只是用来实现异步方法,使用的方式也类似。

接下来使用Actions完成上一个案例实现:

index.js

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
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

const store = new Vuex.Store({
state: {
message: '原始信息'
},
mutations:{
showMessage(state) {
state.message = '更新信息' //同步方法
}
},
actions:{
updateMessage(context) { //参数为context(上下文)
setTimeout(() => { //实现异步操作(请求)
context.commit('showMessage') //调用mutations中的同步方法
}, 1000)
}
},
getters:{
},
modules:{}
})

export default store

说明:

  • context:上下文。是actions定义异步方法中的参数,是和state对象具有相同的方法和属性的对象。所以可以通过context去进行commit相关的操作, 也可以获取context.state等。

  • 异步方法(请求)的实现思路如下:

    在actions中定义方法去实现异步请求,如果需要改变状态量,则仍需要调用mutations中的同步方法,在同步方法中实现对状态量的更新。

    注意:如果跳过mutations直接改变状态量是没有效果的。

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
<template>
<div id="app">
<h2>我是App标题</h2>
<h2>{{$store.state.message}}</h2>
<button @click="update">更新</button>
</div>
</template>

<script>

export default {
name: 'App',
methods: {
update() {
return this.$store.dispatch('updateMessage') //使用$store.dispatch调用actions中定义的异步方法
}
}
}
</script>

<style>

</style>

说明:

在Vue实例中通过$store.dispatch('方法名')去调用actions中定义的异步方法。

效果如下:

可以看到插件可以正常跟踪到状态量的变化情况。

payload

同样的,actions中也支持payload对象的参数传递。

案例:vue实例传递一条消息(以对象的形式),actions进行接收并传递给mutations,最后由mutations中的方法执行控制台打印。

index.js

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
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

const store = new Vuex.Store({
state: {
message: '原始信息'
},
mutations:{
showMessage(state, payload) { //接收从actions中的updateMessage方法传来的payload
state.message = '更新信息'
console.log(payload.message) //打印payload中的信息
}
},
actions:{
updateMessage(context, payload) { //接收从Vue实例中传来的payload
setTimeout(() => {
context.commit('showMessage', payload) //传递给mutations中的showMessage方法
}, 1000)
}
},
getters:{
},
modules:{}
})

export default store

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
<template>
<div id="app">
<h2>我是App标题</h2>
<h2>{{$store.state.message}}</h2>
<button @click="update">更新</button>
</div>
</template>

<script>

export default {
name: 'App',
methods: {
update() {
return this.$store.dispatch({
type: 'updateMessage', //指定传递给actions中定义的updateMessage方法
message: '原始信息已被修改'
})
}
}
}
</script>

<style>

</style>

(效果展示省略)

Promise的应用

如果希望Vue实例在调用异步方法(请求)时返回一些执行结果(用以判断请求是否成功)可以结合Promise使用。

案例需求同上:

index.js

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
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

const store = new Vuex.Store({
state: {
message: '原始信息'
},
mutations:{
showMessage(state) {
state.message = '更新信息'
}
},
actions:{
updateMessage(context) {
return new Promise((resolve, reject) => {
setTimeout(() => {
context.commit('showMessage')
resolve('信息已完成更新') //模拟异步请求到的数据
}, 1000)
})
}
},
getters:{
},
modules:{}
})

export default store

说明:

在updateMessage方法中返回一个新的Promise对象,内部实现异步请求的操作,并通过resolve将数据传递给then进一步处理。但是then会在Vue实例中实现。

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
<template>
<div id="app">
<h2>我是App标题</h2>
<h2>{{$store.state.message}}</h2>
<button @click="update">更新</button>
</div>
</template>

<script>

export default {
name: 'App',
methods: {
update() {
return this.$store.dispatch('updateMessage').then((data) => {
console.log(data) //对请求到的数据进行处理
})
}
}
}
</script>

<style>

</style>

重点理解:

update方法返回了actions中定义的updateMessage方法,而该方法返回了一个新的Promise对象,所以在这里this.$store.dispatch('updateMessage')就是一个Promise对象,因而可以在其后直接链接一个then函数,内部的data参数就接收了来自actions中利用Promise异步请求来的数据。

效果展示:

modules

Vue实例中很多状态量都是交由Vuex中的store来保管,但是在当应用非常复杂时,store对象就会变得非常臃肿,这时就可以使用modules对store对象中的状态量进行分模块进行抽离。

每个模块都有自己的statemutationsactionsgetters,也有modules(但一般不会再进行模块抽离)

基本使用

可以直接在store中定义各类modules,但是建议将其抽离出来单独写,然后在store中注册,格式如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const moduleA = {  //模块A
state: {...},
mutations: {...},
actions: {...},
getters: {...},
}

const moduleB = { //模块B
state: {...},
mutations: {...},
actions: {...},
getters: {...},
}

const store = new Vuex.store({
modules: {
a: moduleA, //注册模块A
b: moduleB, //注册模块B
}
})

state使用

index.js

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
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

const ModuleTest = {
state: {
message: '我是模块信息' //定义state中的message
},
mutations:{},
actions:{},
getters:{},
}

const store = new Vuex.Store({
state: {},
mutations:{},
actions:{},
getters:{},
modules:{
ModuleTest: ModuleTest
}
})

export default store

App.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<template>
<div id="app">
<h2>我是App标题</h2>
<h2>{{$store.state.ModuleTest.message}}</h2>
</div>
</template>

<script>

export default {
name: 'App',
}
</script>

<style>

</style>

说明:

用法:$store.state.模块名.属性名

效果如下:

getters使用

index.js

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
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

const ModuleTest = {
state: {
counter: 1 //模块中的数据
},
mutations:{},
actions:{},
getters:{
mulModule(state) {
return state.counter*100 //修改模块中的数据
},
mulRoot(state, mulModule, rootState) { //注意参数位置
return rootState.counter*100 //修改根中的数据
}
},
}

const store = new Vuex.Store({
state: {
counter: 2 //根中的数据
},
mutations:{},
actions:{},
getters:{},
modules:{
ModuleTest: ModuleTest
}
})

export default store

说明:

模块中getters方法内参数的定义:方法名(state, 其他方法名, rootState)

注意参数位置不能颠倒。

App.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<template>
<div id="app">
<h2>我是App标题</h2>
<h2>Modules原始数据:{{$store.state.ModuleTest.counter}}</h2>
<h2>修改Modules数据:{{$store.getters.mulModule}}</h2>
<h2>Root原始数据:{{$store.state.counter}}</h2>
<h2>修改Root数据:{{$store.getters.mulRoot}}</h2>
</div>
</template>

<script>

export default {
name: 'App',
}
</script>

<style>

</style>

说明:

在访问模块中的getters方法时不用特意指明模块,直接指出方法名就好,格式:

$store.getters.方法名

效果如下:

mutations使用

index.js

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
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

const ModuleTest = {
state: {
counter: 1
},
mutations:{
increase(state, payload) {
console.log(payload.message)
return state.counter += 1
},
decrease(state, payload) {
console.log(payload.message)
return state.counter -= 1
}
},
actions:{},
getters:{},
}

const store = new Vuex.Store({
state: {},
mutations:{},
actions:{},
getters:{},
modules:{
ModuleTest: ModuleTest
}
})

export default store

说明:

操作与store中的mutations一致。

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
<template>
<div id="app">
<h2>我是App标题</h2>
<h2>Modules数据:{{$store.state.ModuleTest.counter}}</h2>
<button @click="add">+</button>
<button @click="reduce">-</button>
</div>
</template>

<script>

export default {
name: 'App',
methods: {
add() {
return this.$store.commit({
type: 'increase',
message: '数据已增加'
})
},
reduce() {
return this.$store.commit({
type: 'decrease',
message: '数据已减少'
})
}
}
}
</script>

<style>

</style>

说明:

在访问模块中的mutations方法时不用特意指明模块,直接指出方法名就好,格式:

  • $store.commit('方法名')
  • $store.commit(payload对象)

效果如下:

actions使用

index.js

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
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

const ModuleTest = {
state: {
message: 'Module原始信息'
},
mutations:{
updateMessage(state) {
state.message = 'Module信息已被修改'
}
},
actions:{
update(context) {
return new Promise((resolve, reject) => {
setTimeout(() => {
context.commit('updateMessage')
resolve('信息修改已完成')
}, 1000)
})
}
},
getters:{},
}

const store = new Vuex.Store({
state: {},
mutations:{},
actions:{},
getters:{},
modules:{
ModuleTest: ModuleTest
}
})

export default store

说明:

操作与store中的actions一致。

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
<template>
<div id="app">
<h2>我是App标题</h2>
<h2>{{$store.state.ModuleTest.message}}</h2>
<button @click="updateM">更新</button>
</div>
</template>

<script>

export default {
name: 'App',
methods: {
updateM() {
return this.$store.dispatch('update').then((data) => {
console.log(data)
})
}
}
}
</script>

<style>

</style>

说明:

在访问模块中的actions方法时不用特意指明模块,直接指出方法名就好,格式:

$store.dispatch('方法名')

效果展示:

项目结构

为了更清晰明了的编写项目,以及后期方便维护,建议将Vuex.Store对象中的mutationsactionsgettersmodules进行抽离,建立新的文件(state状态量仍然在index.js文件中):

index.js

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
import Vue from 'vue'
import Vuex from 'vuex'

import mutations from './mutations'
import actions from './actions'
import getters from './getters'
import ModuleA from './modules/ModuleA'
import ModuleB from './modules/ModuleB'

Vue.use(Vuex)

const state = {
message: '项目结构'
}

const store = new Vuex.Store({
state,
mutations,
actions,
getters,
modules:{
ModuleA: ModuleA,
ModuleB: ModuleB,
}
})

export default store

mutations.js

1
2
3
4
export default {
Mmethod1(state, payload) {...},
Mmethod2(state, payload) {...},
}

actions.js

1
2
3
4
export default {
Amethod1(context) {...},
Amethod2(context) {...},
}

getters.js

1
2
3
4
5
export default {
Gmethod1(state) {...},
Gmethod2(state, Gmethod1) {...},
Gmethod3(state, Gmethod2, rootState) {...},
}

模块会再新建一个文件夹:

./modules/ModuleA

1
2
3
4
5
6
export default {
state: {...},
mutations: {...},
actions: {...},
getters: {...},
}

./modules/ModuleB

1
2
3
4
5
6
export default {
state: {...},
mutations: {...},
actions: {...},
getters: {...},
}

后记

在做项目的时候也需要注意一些代码结构的细节,到后期代码量增加的时候便于维护。