Vuex是由Vue官方开发的一个专为Vue.js开发的状态管理库。Vuex可以集中存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
Vuex基于“单向数据流”理念:
Vuex的工作模式如下:
Vuex应用的核心就是Store(仓库)。Store基本上就是一个容器,它包含着应用中大部分的状态(State)。
使用Vuex也可以实现任意组件间通信。但是官方在文档中提到,如果要构建一个简单的网页应用,使用Vuex可能是繁琐冗余的。
如果应用够简单,最好不要使用Vuex。一个简单的Store模式就足够了。
如果需要构建一个中大型单页应用,就需要考虑如何更好地在组件外部管理状态,Vuex将会成为自然而然的选择。
安装 Vuex
在Node.js环境下,可以运行下方命令安装Vuex:
npm i vuex
但是需要注意的是,目前不指定Vuex版本的情况下,安装的是vuex@4
,而vuex@4
适用于Vue3,适用于Vue2的Vuex版本是vuex@3
。
# 安装适用于 Vue2 的 Vuex 3
npm i vuex@3
安装好后,就可以在项目中导入Vuex:
import Vuex from 'vuex'
导入Vuex后,需要在Vue中开启Vuex插件:
Vue.use(Vuex)
配置 Vuex
Vuex的基本配置如下(Vuex配置文件可以是项目根目录下的vuex/store.js
或store/index.js
):
import Vue from 'vue'
import Vuex from 'vuex' // 引入 Vuex
Vue.use(Vuex) // 使用 Vuex
// Actions 用于响应组件中的动作
const actions = {
/* ... */
}
// Mutations 用于操作数据
const mutations = {
/* ... */
}
// State 用于存储数据
const state = {
/* ... */
}
// Getters 用于将 State 中的数据进行加工
const getters = {
/* ... */
}
// 创建并导出 Store
export default new Vuex.Store({
actions,
mutations,
state,
getters,
})
创建好Store之后,需要将其导入到main.js
中,并配置在Vue实例上:
import Vue from 'vue'
import store from './store'
/* ... */
new Vue({
/* ... */
store,
/* ... */
}).$mount('#app')
配置好后,当前Vue实例,以及Vue实例下的所有组件,都能通过this.$store
访问Store的API。
使用 Store
Store中,常用的API如下:
$store.dispatch()
:将数据分发给对应的Action。$store.commit()
:将数据(载荷)提交给对应的Mutation.
Store中,常用的数据对象如下:
$store.state
:访问State中的状态数据(可以认为是一个全局的data
)。$store.getters
:对State中的状态数据进行相应处理,并获取对应的处理结果(可以认为是一个全局的,没有Setter的computed
)。
dispatch
$store.dispatch(actionName, data)
:
actionName
:指定一个Action的名称,String
类型。对应store
配置中actions
配置对象下的一个相同名称的方法actionName()
。data
:分发给actionName()
的数据,任意类型。actionName()
使用第2个形参value
接收。
每一个Action,在actions
中都有一个与之对应的方法。在组件实例中,使用$store.dispatch()
来将数据分发给对应的Action方法处理。每个Action都有一个唯一的actionName
。
通常,actionName是以小驼峰规则命名。
this.$store.dispatch('demoAction', value)
demoAction
对应的Action:
const actions = {
/* ... */
demoAction(context, value) {
/* demoAction的处理逻辑... */
},
/* ... */
}
context
是一个上下文对象,每一个Action都会接收到这个参数。context
封装了$store
中的一些方法。通过context
,可以将当前处理转发给其它Action,或是将处理提交到某个Mutation。
context
中封装了如下方法和对象:
context.dispatch()
context.commit()
context.state
context.getters
这些方法和对象的用法与$store
中的相同。
context
可以看作是一个小型的$store
,它封装了$store
中常用的方法和对象。使用者可以根据处理逻辑(上下文)调用context
中的方法,所以才命名为context
。在Action中,应该将
context.state
视为只读的。因为Action的职责不是对状态进行修改,并且Vue.js devtools(Vue.js开发者工具)捕获不到Action对context.state
的修改(尽管这些修改可能会生效),所以应该尽量避免在Action中对context.state
的修改。
通常情况下,Action是用来对数据进行一些校验或简单的处理,然后将数据转派给下一个Action或提交给某个Mutation。
$store.dispatch()
在调用时可以仅传递一个参数,也就是actionName
。例如:
this.$store.dispatch('plusOne')
commit
$store.commit(mutationName, payload)
:
mutationName
:指定一个Mutation的名称(事件类型),String
类型。对应store
配置中mutations
配置对象下的一个相同名称的方法mutationName()
。payload
:提交给mutationName()
的数据(载荷),任意类型。mutationName()
使用第2个形参value
接收。
通常,mutationName是以全大写+下划线规则命名。
$store.commit()
的用法和$store.dispatch()
十分相似,不同的是$store.commit()
是将载荷提交给某个Mutation。
this.$store.commit('demoMutation', payload)
demoMutation
对应的Mutation:
const mutations = {
/* ... */
demoMutation(state, payload) {
/* demoMutation的处理逻辑... */
},
/* ... */
}
state
即对应Store配置中的state
配置对象(相当于$store.state
)。每一个Mutation都会接收到这个state
对象。通过state
对象,Mutation可以获取State中的状态数据,并对状态数据进行编辑。
与Action不同,Mutation通常是作为状态数据处理的角色存在。Mutation可以直接修改State中的数据;而Action通常不修改State中的数据,Action一般只是读取State中的数据。
Action不用作修改数据还有一个原因,如果在Action中修改State中的数据,Vue.js devtools并不能捕获到这些修改。即使在Action对State的修改能生效,也不建议在Action中对State进行修改,在Actions中,应该将State(
context.$state
)视为只读的。
一般情况下(官方给的示意图中),Store对数据的处理流程是:$store.dispatch
$\Rightarrow$ $store.commit
。实际上,对一些简单的操作,不需要对传递的数据进行校验或处理的情况下,可以在组件实例中直接调用$store.commit
将数据提交给Mutation处理。
$store.commit()
在调用时可以仅传递一个参数,也就是mutationName
。例如:
this.$store.commit('plusOne')
state
在组件中读取State中的数据,可以通过this.$store.state.dataName
的形式读取(dataName
指要读取的数据)。需要注意的是,在组件实例中不要修改State中的数据。也就是说,在组件实例中应该将this.$store.state
视为只读。
例如在Store的state
配置项中定义一个状态数据:
const state = {
siteTitle: 0, // 网站的标题
}
在组件实例中获取state
中的数据:
this.$store.state.siteTitle
在组件实例中推荐使用computed
来获取state
中的数据:
export default {
/* ... */
computed: {
siteTitle() {
return this.$store.state.siteTitle
},
},
/* ... */
}
也可以通过computed
的Setter来修改state
中的数据:
export default {
/* ... */
computed: {
/* ... */
siteTitle: {
get() {
return this.$store.state.siteTitle
},
set(payload) {
this.$store.commit('SET_SITE_TITLE', payload)
}
},
/* ... */
},
/* ... */
}
const mutations = {
/* ... */
SET_SITE_TITLE(state, payload) {
state.siteTitle = payload
}
/* ... */
}
接着,就可以在组件实例中,像使用一般的数据一样,对siteTitle
进行各种操作。
getters
$store.getters
就像是一个全局的computed
,可以在获取State中的数据之前,对数据进行一些处理。但与computed
不同的是,getters
只能用于获取,不能用于修改数据。
在Store的getters
配置中,每个getter
以函数的形式被定义,且每个getter
接收一个state
参数。这个state
参数用于获取State中的数据。
每个getter
都有一个唯一的名称,并且就像computed
一样,在Vue实例中使用this.$store.getterName
来获取相应的getter
,getterName
与getter
定义时的函数名称相同。getter
同样也是使用返回值来确定值。
const getters = {
/* ... */
demoGetter(state) {
/* demoGetter 的处理逻辑... */
return /* demoGetter 的值 */
},
/* ... */
}
例如要获取一个转为大写的siteTitle
:
const getters = {
siteTitleUpperCase(state) {
return state.siteTitle.toUpperCase()
},
}
在组件实例中获取该值:
this.$store.gettets.siteTitleUpperCase
同样可以使用computed
来读取该值:
siteTitleUpperCase() {
return this.$store.gettets.siteTitleUpperCase
}
Vuex 映射
在组件实例中,使用Store中的state
和getter
时,配合computed
可以使代码更加精简。而Vuex原型正好提供了一些辅助的API,可以在组件实例中快速地为state
和getters
中的数据创建对应的computed
。
mapState()
mapGetters()
mapMutations()
mapActions()
在使用这些辅助函数之前,需要先引入。这些辅助函数的引入方法都是相同的,下面进行例举了辅助函数的引入方式:
import { mapState } from 'vuex' // 引入 mapState
import { mapState, mapGetters } from 'vuex' // 引入 mapState 和 mapGetters
mapState
mapState()
可以将State中的数据映射到组件实例中的computed
中。
computed: mapState({
computedAttr1: 'stateAttr1',
computedAttr2: 'stateAttr2',
computedAttr3: 'stateAttr3',
/* ... */
}),
或者使用对象扩展运算符:
computed: {
...mapState({
computedAttr1: 'stateAttr1',
computedAttr2: 'stateAttr2',
computedAttr3: 'stateAttr3',
/* ... */
}),
/* ... */
}
上方所示的两个mapState()
相当于在computed
中进行如下定义:
computed: {
computedAttr1() {
return this.$store.state.stateAttr1
},
computedAttr2() {
return this.$store.state.stateAttr2
},
computedAttr3() {
return this.$store.state.stateAttr3
},
/* ... */
}
也就是说,mapState()
返回的是一个类似于computed
配置对象的对象。这个对象中的每个属性都是函数类型,相当于一个个设置了Setter的computed
属性。
使用mapState()
生成的computed
被称为vuex bindings
(可以在Vue开发者工具中查看)。
mapState()
可以传入两种类型的参数:
-
一种就是如上所示的
Object
类型参数。传入的对象中,其每个属性的
key
是作为实例中的computed
属性名称,每个属性的value
以String
类型的形式指定了State中某个属性的名称。该方式通过传入对象中的每个属性的
value
指定State中的属性,然后以每个属性的key
作为它们的computed
属性来生成vuex bindings
。 -
另一种方式就是传入字符串数组类型的参数。
mapState([ 'stateAttr1', 'stateAttr2', 'stateAttr3', /* ... */ ]),
这种方式通过字符串数组指定一系列要作为
computed
(vuex binding
)属性使用的State属性。这种方式可以直接使用对应State属性的名称来使用这些vuex binding
属性。上方
mapState()
相当于在computed
中配置了以下内容:stateAttr1() { return this.$store.state.stateAttr1 }, stateAttr2() { return this.$store.state.stateAttr2 }, stateAttr3() { return this.$store.state.stateAttr3 },
mapGetters
mapGetters()
可以将getters
中的数据映射到组件实例中的computed
中。生成的属性同样被称为vuex bindings
。
mapGetters()
的用法和mapState()
几乎相同,它们都可以传入两种类型的参数,并且返回值也是一个属性都为函数类型的对象。
mapGetters({
gettersAttr1: 'gettersAttr1',
gettersAttr2: 'gettersAttr2',
gettersAttr3: 'gettersAttr3',
/* ... */
})
mapGetters([
'gettersAttr1',
'gettersAttr2',
'gettersAttr3',
/* ... */
])
上述两种使用方式,都是相当于在computed
进行如下配置:
computed: {
gettersAttr1() {
return this.$store.getters.gettersAttr1
},
gettersAttr2() {
return this.$store.getters.gettersAttr2
},
gettersAttr3() {
return this.$store.getters.gettersAttr3
},
/* ... */
}
mapMutations
mapMutations()
用于在methods
中快速生成提交(commit
)对应Mutations事件的方法。mapMutations()
同样拥有两种形参类型。
mapMutations()
作用于methods
,但是用法基本上与mapState()
和mapGetters()
相同。并且mapMutations()
传入的也是一个属性为function
类型的对象。
methods: mapMutations({
increment: 'INCREMENT',
decrement: 'DECREMENT',
}),
相当于在methods
中进行如下配置:
methods: {
increment(value) {
this.$store.commit('INCREMENT', value)
},
decrement(value) {
this.$store.commit('DECREMENT', value)
},
}
所以在调用或者绑定事件时,需要以increment()
或increment(value)
的形式。否则,以increment
(如@click="increment"
)的形式绑定事件,传入的value
参数就是当前绑定事件的对象(可能会造成数据错误)。
mapMutations()
数组参数写法如下:
methods: mapMutations([
'INCREMENT',
'DECREMENT',
]),
相当于:
methods: {
INCREMENT(value) {
this.$store.commit('INCREMENT', value)
},
DECREMENT(value) {
this.$store.commit('DECREMENT', value)
},
}
mapActions
mapActions()
用于在methods
中快速生成分发(dispatch
)对应Action的方法。
mapMutations()
的用法基本上与mapMutations()
相同。
methods: mapActions({
increment: 'increment',
decrement:'decrement',
}),
methods: mapActions([
'increment',
'decrement',
]),
上述两种方式相当于:
methods: {
increment(value) {
this.$store.dispatch('increment', value)
},
decrement(value) {
this.$store.dispatch('decrement', value)
},
}
同样使用increment()
或increment(value)
的形式调用或绑定事件。
评论