记录工作中的点点滴滴

手把手教你怎么使用Vuex-Vuex系列之Vuex核心介绍

手把手教你怎么使用Vuex之Vuex核心介绍

通过上一篇大概了解了Vuex是什么,这一篇来大概讲解一下Vuex的核心部分。

一.开始

每一个 Vuex 应用的核心就是 store(仓库)。”store” 基本上就是一个容器,它包含着你的应用中大部分的状态(state)。Vuex 和单纯的全局对象有以下两点不同:

  1. Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。
  2. 你不能直接改变 store 中的状态。改变 store 中的状态的唯一途径就是显式地提交(commit) mutations。这样使得我们可以方便地跟踪每一个状态的变化,从而让我们能够实现一些工具帮助我们更好地了解我们的应用。

二.Store

安装完Vuex之后,让我们创建一个Store。

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: {
},
actions: {
},
mutations: {
},
getters: {
},
modules: {
}
})
export default store

上述代码就包含了定义Vuex Store时的5个关键属性,也就是我们这一篇文章要讲的核心介绍。

三.State

在Vue组件中如果想要获取Vuex的状态,都需要从state中获取。最简单的方式就是在计算属性中返回state的某个状态:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 创建一个 Store
const store = new Vuex.Store({
state: {
count: 0
}
})
// 创建一个 Counter 组件
const Counter = {
template: `<div>{{ count }}</div>`,
computed: {
count () {
return store.state.count
}
}
}

如果你想在每个子组件中都调用state,可以在根组件中注册store选项,vuex就会提供了一种机制将状态从根组件『注入』到每一个子组件中(需调用 Vue.use(Vuex))

store/index.js:

1
2
3
4
5
6
7
8
9
10
11
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const store = new Vuex.Store({
state: {
...
}
...
})
export default store

根组件:

1
2
3
4
5
6
import store from './store'
const app = new Vue({
// 把 store 对象提供给 “store” 选项,这可以把 store 的实例注入所有的子组件
store
});

子组件:

1
2
3
4
5
6
7
8
const Counter = {
template: `<div>{{ count }}</div>`,
computed: {
count () {
return this.$store.state.count
}
}
}

在Vuex 2中新增了一个mapState辅助函数,当一个组件需要获取多个状态时,可以直接在函数中声明。
子组件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import { mapState } from 'vuex'
export default {
// ...
computed: mapState({
// 箭头函数可使代码更简练
count: state => state.count,
// 传字符串参数 'count' 等同于 `state => state.count`
countAlias: 'count',
// 为了能够使用 `this` 获取局部状态,必须使用常规函数
countPlusLocalState (state) {
return state.count + this.localCount
}
})
}

当计算属性与子节点相同时,可以怎么写:

1
2
3
4
computed: mapState([
// 映射 this.count 为 store.state.count
'count'
])

四.Getters

当state中的某些状态在各个组件中都被频繁使用,如果在每个组件中都声明一次,将会变得非常繁琐。因此便有了getters来帮助我们解决这个问题,你也可以把它看做Vuex的计算属性或者理解为定义一个别名使我们能够在组件中方便的调用。

场景:比如计算器案例中我们需要判断当前的计数是奇数还是偶数,并在页面上显示出来。
我们可以这样做:

创建evenOrOdd变量

在getter.js里创建一个 evenOrOdd变量,判断是奇数还是偶数

1
export const evenOrOdd = state => state.count % 2 === 0 ? 'even' : 'odd'

使用evenOrOdd变量

然后在计算器项目中的任何一个页面上显示 当前计数是奇数还是偶数。
在Vuex 2中提供了一个mapGetters辅助函数用于有多个状态需要获取的情况。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<template>
<div id="app">
Clicked: {{ $store.state.count }} times, count is {{ evenOrOdd }}.
---
</div>
</div>
</template>
<script>
import { mapGetters, mapActions } from 'vuex'
export default {
computed : mappGetters([
'evenOrOdd'
])
}
</script>

在counter.vue组件里这样使用即可。

五.Mutations

更改 Vuex 的 store 中的状态的唯一方法是提交 mutation。
mutation不能直接调用,而要通过相应的 type 调用相应的store.commit方法:

1
store.commit('increment')

Vuex 中的 mutations 非常类似于事件:每个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler)。
这个回调函数就是我们实际进行状态更改的地方,并且它会接受 state 作为第一个参数。我们可以向store.commit传入额外的参数payload,payload可以是一个对象,这样可以包含多个字段并且记录的 mutation 会更易读。

1
2
3
4
5
6
7
// ...
mutations: {
increment (state, n) {
state.count += n
}
}
store.commit('increment', 10)

使用常量替代 Mutation 事件类型

使用常量替代 mutation 事件类型在各种 Flux 实现中是很常见的模式。这样可以使 linter 之类的工具发挥作用,同时把这些常量放在单独的文件中可以让你的代码合作者对整个 app 包含的 mutation 一目了然:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// mutation-types.js
export const SOME_MUTATION = 'SOME_MUTATION'
// store.js
import Vuex from 'vuex'
import { SOME_MUTATION } from './mutation-types'
const store = new Vuex.Store({
state: { ... },
mutations: {
// 我们可以使用 ES2015 风格的计算属性命名功能来使用一个常量作为函数名
[SOME_MUTATION] (state) {
// mutate state
}
}
})

说明:用不用常量取决于你 —— 在需要多人协作的大型项目中,这会很有帮助。但如果你不喜欢,你完全可以不这样做。

在组件中提交 Mutations

你可以在组件中使用 this.$store.commit(‘xxx’) 提交 mutation,或者使用 mapMutations 辅助函数将组件中的 methods 映射为 store.commit 调用(需要在根节点注入 store)。

1
2
3
4
5
6
7
8
9
10
11
12
13
import { mapMutations } from 'vuex'
export default {
// ...
methods: {
...mapMutations([
'increment' // 映射 this.increment() 为 this.$store.commit('increment')
]),
...mapMutations({
add: 'increment' // 映射 this.add() 为 this.$store.commit('increment')
})
}
}

每当你需要管理状态时,每个改变都可以定义成一个命令,叫Mutation,通过在store中定义命令和执行器,要改变状态就通过store发出这个命令。

Mutation强调同步操作,至于你要对管理状态作出异步的操作就得看看Action

六.Action

Action相对于mutation,有以下不同:

  • Action 提交的是 mutation,而不是直接变更状态。
  • Action 可以包含任意异步操作。

在组件中vuex配置相关action,方法的实现通常是在异步操作中组合一些mutation,当然,如果Action用在同步场景,你可以把Action理解成用于组合Mutation的大Mutation,另外 ,action方法的第一个参数总是store。

1
2
3
4
5
6
7
// 定义Actions
const actions = {
actionName({ commit }) {
//dosomething
commit('mutationName')
}
}

在组件中使用

1
2
3
4
5
6
7
8
import {mapActions} from 'vuex'
//我是一个组件
export default {
methods: mapActions([
'actionName',
])
}

简单同步操作用Mutation还是Action?

从语法来说,你喜欢哪个都行,不过统一开发思路,在组件内,只跟action打交道,然后action里面再去转发Mutation,这样开发体验统一,你无须访问store,好维护。官方指定的规则也是只用action。

七.Modules

如果应用简单,mutation、action、state各自定义一个总文件,如果多模块,那么在每个模块定义各自的这三个部分,借助vuex store的modules配置即可。

结束

关于Vuex的核心概念已经介绍得差不多了,下一篇就进入撸码阶段,会一步一步的介绍怎么使用Vuex去开发一个计数器的demo。