Vuex
- 一. 介绍
- Vuex 是什么
- 安装
- 引入
- 使用
- 示例
- 二、state
- 1. 在组件中展示状态
- 2. mapState 辅助函数
- 3. 对象运算展开符
- 4. 组件仍然保有局部状态
- 三、Getter
- 1. 组件中通过属性访问
- 2. 组件中通过方法访问
- 3. mapGetters 辅助函数
- 四、Mutation
- 1. 提交载荷(Payload)
- 2. 对象风格的提交方式
- 3. 使用常量替代 Mutation 事件类型
- 4. Mutation 必须是同步函数
- 5. 在组件中提交Mutation
- 五、Action
- 1. 注册action
- 2. 分发action
- 2.1 action 里内部可执行异步
- 2.2 action 支持载荷方式和对象方式分发
- 2.3 在组件中分发 Action
- 3. 组合 Action
- 六、Module
- 1. 使用
- 2. 命名空间
- 2.1 在带命名空间的模块内访问全局内容
- 2.2 在带命名空间的模块注册全局 action
- 2.3 在组件中与 mapState, mapGetters, mapActions 和 mapMutations 连用
- 3. 模块动态注册
- 3.1 注册模块
- 3.2 注册嵌套模块
- 3.2 卸载模块
- 3.3 判断模块是否被注册到store
- 3.4 保留 state
- 4. 带命名空间的绑定函数
- 4.1 使用模块的空间名称字符串
- 4.2 使用createNamespacedHelpers
- 5. 模块重用
- 七. 总结
- 八. 项目结构
一. 介绍
Vuex 是什么
Vuex是 Vue 的 共享状态管理,小型项目使用 store模式 即可,中大型项目需要使用 VueX。
安装
npm install vuex --save
注意:在vue2中,要用 vuex 的3版本;在vue3中,要用vuex的4版本。
引入
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
使用
Vuex应用的核心是 store(仓库),它包含应用中的状态state。Vuex和单纯的全局对象有两种不同:
- Vuex 的状态存储是 响应式 的,若store中的状态发生改变,相应的组件也会更新;
- 不能直接改变store中的状态,要通过mutation中的commit进行提交。
示例
下面是一个创建store并使用的示例:
- 新建 vuex-store.js(与main.js同目录):
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
// state: 存放变量
state: {
count: 1
},
// mutation: 存放方法
mutations: {
increment (state) {
state.count++
}
}
})
- 组件中引入:
需要在Hello.vue中引入store才可以使用vuex。
<template>
<div class="hello">{{state.count}}</div>
</template>
<script>
import store from '../vuex-store.js'
export default {
name: 'HelloWorld',
// 变量通常在计算属性中返回
computed: {
state () {
return store.state
}
}
methods: {
increase () {
store.commit('increment')
}
}
}
</script>
- 全局注入Store机制:
即使在组件中不引入store也可以使用vuex。
- 首先,在main.js中注入:
import store from './vuex-store'
new Vue({
el: '#app',
// 注入: 把 store 对象提供给 “store” 选项,这可以把 store 的实例注入所有的子组件
store,
router,
components: { App },
template: '<App/>'
})
- 组件中直接使用(子组件能通过 this.$store 访问到):
<template>
<div>
<div class="hello">{{ $store.state.count }}</div>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
methods: {
increase () {
this.$store.commit('increment')
console.log(this.$store.state.count)
}
}
}
</script>
二、state
1. 在组件中展示状态
从 store 实例中 读取状态是通过 computed计算属性 中返回某个状态:
computed: {
count() {
return this.$store.state.count;
}
}
2. mapState 辅助函数
当一个组件需要获取多个状态的时候,将这些状态都声明为计算属性会有些重复和冗余,我们可以使用 mapState 辅助函数帮助我们生成计算属性:
<template>
<div>
<div class="hello">{{ count }}</div>
<div class="hello">{{ countAlias }}</div>
<div class="hello">{{ total }}</div>
</div>
</template>
<script>
// 引入辅助函数 mapState
import {mapState} from 'vuex'
export default {
name: 'HelloWorld',
data () {
return {
num: 10
}
},
computed: mapState({
// 箭头函数可使代码更简练
count: state => state.count,
// 传字符串参数 'count' 等同于 `state => state.count`
countAlias: 'count',
// 为了能够使用 `this` 获取局部状态,必须使用常规函数
total (state) {
return state.count + this.num
}
})
}
</script>
注意:当映射的 计算属性 的名称与 state 中的变量名相同时,可以给 mapState 传一个字符串数组:
computed: mapState([
// 映射 this.count 为 store.state.count
'count'
])
3. 对象运算展开符
mapState 函数返回的是一个对象,当 局部计算属性 与 mapState 混合使用,可以使用 对象展开运算符 将此对象混入到外部对象中:
computed: {
showNum () {
return `数量是${this.num}`
},
// 对象展开运算符:将此对象混入到外部对象中
...mapState({
count: state => state.count
})
}
4. 组件仍然保有局部状态
使用 Vuex 并不意味着你需要将所有的状态放入 Vuex。虽然将所有的状态放到 Vuex 会使状态变化更显式和易调试,但也会使代码变得冗长和不直观。如果有些状态严格属于单个组件,最好还是作为组件的局部状态。你应该根据你的应用开发需要进行权衡和确定。
三、Getter
有时候我们需要从 store 中的 state 中派生出一些状态,例如 对列表进行过滤并计数:
vuex-store.js:
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
todos: [
{ id: 1, text: '文案1', done: true },
{ id: 2, text: '文案2', done: false }
]
}
})
组件:
computed: {
doneTodosCount () {
return this.$store.state.todos.filter(todo => todo.done).length
}
}
如果有多个组件需要用到此属性,我们要么复制这个函数,或者抽取到一个共享函数然后在多处导入它——无论哪种方式都不是很理想。
Vuex 允许我们在 store 中定义“getter”(可以认为是 store 的计算属性)。就像计算属性一样,getter 的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。
vuex-store.js:
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
todos: [
{ id: 1, text: '文案1', done: true },
{ id: 2, text: '文案2', done: false }
]
},
getters: {
doneTodos: state => {
return state.todos.filter(todo => todo.done)
}
}
})
1. 组件中通过属性访问
Getter 会暴露为 store.getters 对象,可以以 属性的形式 访问这些值:
<div>{{$store.getters.doneTodos.length}}</div>
this.$store.getters.doneTodos // -> [{ id: 1, text: '文案1', done: true }]
this.$store.getters.doneTodos.length // -> 1
getters 的参数:
- 第一个参数 :state ;
- 第二个参数 :getters ;
getters: {
doneTodos: state => {
return state.todos.filter(todo => todo.done)
},
doneTodosCount: (state, getters) => {
return getters.doneTodos.length
}
}
我们可以任何组件中使用它:
this.$store.getters.doneTodosCount // -> 1
注意:getter 在通过属性访问时是作为 Vue 的响应式系统的一部分缓存。
2. 组件中通过方法访问
通过让 getter 返回一个函数,来实现给 getter 传参(对 store 里的数组进行查询时非常有用)。
vuex-store.js:
getters: {
// ...
getTodoById: (state) => (id) => {
return state.todos.find(todo => todo.id === id)
}
}
组件中:
console.log(this.$store.getters.getTodosById(2));
-> { id: 2, text: '文案2', done: false }
注意:getter 在通过方法访问时,每次都会去进行调用,而不会项属性访问那样去缓存结果。
3. mapGetters 辅助函数
将 store 中的 getter 映射到局部计算属性,使用方法和 mapState 相同。
<template>
<div>
<div>{{ doneTodosCount }}</div>
</div>
</template>
<script>
// 1.引入辅助函数 mapState
import {mapGetters} from 'vuex'
export default {
name: 'HelloGetter',
// 2. 传入数组
computed: mapGetters([
'doneTodos',
'doneTodosCount',
'getTodosById'
]),
mounted () {
// 3. 无论是getters中的属性和方法都可以直接使用
console.log(this.doneTodos)
console.log(this.getTodosById(2))
},
}
</script>
如果想将一个 getter 属性另取一个名字,可以使用对象形式:
created () {
console.log(this.getData(2))
},
computed: {
...mapGetters({
// 把 `this.getData` 映射为 `this.$store.getters.getTodosById`
getData: 'getTodosById'
})
}
四、Mutation
更改 Vuex 的 store 中的状态的唯一方法是提交 mutation。
Vuex 中的 mutation 非常类似于事件:每个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler)。这个回调函数就是我们实际进行状态更改的地方,并且它会接受 state 作为第一个参数:
export default new Vuex.Store({
state: {
count: 1
},
mutations: {
increment (state) {
// 变更状态
state.count++
}
}
})
不能直接调用一个 mutation handler,当触发一个类型为 increment 的 mutation 时,需要以相应的 type 调用 store.commit 方法:
this.$store.commit('increment')
1. 提交载荷(Payload)
mutations 中的方法,第一个参数为 state,第二个参数就叫做 载荷(payload),通常为对象并且包含多个字段。
// ...
mutations: {
increment (state, payload) {
state.count += payload.amount
}
}
在组件中调用:
this.$store.commit('increment', {
amount: 5
})
2. 对象风格的提交方式
提交 mutation 的另一种方式是直接使用包含 type 属性的对象:
this.$store.commit({
type: 'increment',
amount: 5
})
处理函数保持不变
3. 使用常量替代 Mutation 事件类型
使用常量替代 mutation 事件类型在各种 Flux 实现中是很常见的模式。这样可以使 linter 之类的工具发挥作用,同时把这些 常量 放在单独的文件中可以让你的代码合作者对整个 app 包含的 mutation 一目了然。
- 创建一个 mutation-types.js:
export const [INCREMENT, DECREMENT] = ['INCREMENT', 'DECREMENT']
- 在 store.js 中引入:
import {INCREMENT, DECREMENT} from './mutation-types'
...
mutations: {
// 可以使用 ES2015 风格的计算属性命名功能来使用一个常量作为函数名
[INCREMENT] (state, payload) {
state.count += payload.amount
},
[DECREMENT] (state, payload) {
state.count -= payload.amount
}
}
- 在组件中使用:
methods: {
onAddClick () {
// INCREMENT
this.$store.commit(INCREMENT, {
amount: 5
})
},
onDelClick () {
// DECREMENT
this.$store.commit({
type: DECREMENT,
amount: 1
})
}
}
用不用常量取决于你——在需要多人协作的大型项目中,这会很有帮助。但如果你不喜欢,你完全可以不这样做。
4. Mutation 必须是同步函数
mutation 中不允许有回调函数,因为任何在回调函数中进行的状态的改变都是不可追踪的。
5. 在组件中提交Mutation
你可以在组件中使用 this.$store.commit(‘xxx’) 提交 mutation,或者使用 mapMutations 辅助函数将组件中的 methods 映射为 store.commit 调用(需要在根节点注入 store)。
vuex-store.js:
export default new Vuex.Store({
state: {
count: 1
},
mutations: {
increment (state) {
state.count++
},
decrement (state) {
state.count--
},
incrementBy (state, payload) {
state.count += payload.amount
},
decrementBy (state, payload) {
state.count -= payload.amount
}
}
})
组件:
import { mapMutations } from 'vuex'
export default {
// ...
methods: {
...mapMutations([
'increment', // 将 `this.increment()` 映射为 `this.$store.commit('increment')`
'decrement', // 将 `this.decrement()` 映射为 `this.$store.commit('decrement')`
// `mapMutations` 也支持载荷:
'incrementBy',
// 将 `this.incrementBy({amount: 5})` 映射为 `this.$store.commit('incrementBy',{amount: 5})`
'decrementBy'
// 将 `this.decrementBy({amount: 1})` 映射为 `this.$store.commit('decrementBy',{amount: 1})`
]),
...mapMutations({
add: 'increment' // 将 `this.add()` 映射为 `this.$store.commit('increment')`
del: 'decrement' // 将 `this.del()` 映射为 `this.$store.commit('decrement')`
})
}
}
五、Action
Action 类似于 mutation,不同在于:
- Action 提交的是 mutation,而不是直接变更状态,而 Mutation是直接改变状态;
- Action 可以包含任意异步操作,Mutation 包含的必须是同步操作。
1. 注册action
actions: {
increment (context) {
context.commit('increment')
}
}
Action 函数接受一个与 store 实例具有相同方法和属性的 context 对象,因此可以调用 context.commit 提交一个 mutation,或者通过 context.state 和 context.getters 来获取 state 和 getters。当我们在之后介绍到 Modules 时,你就知道 context 对象为什么不是 store 实例本身了。
通常会使用ES5参数解构来简化代码:
actions: {
increment ({ commit }) {
commit('increment')
}
}
2. 分发action
Action 通过 store.dispatch 方法触发:
this.$store.dispatch('increment')
2.1 action 里内部可执行异步
actions: {
incrementAsync ({ commit }) {
setTimeout(() => {
commit('increment')
}, 300)
}
}
2.2 action 支持载荷方式和对象方式分发
mutations: {
increment (state, payload) {
state.count += payload.amount
}
},
actions: {
increment ({ commit }, payload) {
commit('increment', payload)
}
}
(1) 载荷方式分发:
this.$store.dispatch('increment', {
amount: 5
})
(2) 对象方式分发:
this.$store.dispatch({
type: 'increment',
amount: 5
})
2.3 在组件中分发 Action
使用 mapActions 辅助函数
// 1. 引入 mapActions 辅助函数
import { mapActions } from 'vuex'
export default {
// ...
methods: {
...mapActions([
'increment', // 将 `this.increment()` 映射为 `this.$store.dispatch('increment')`
// `mapActions` 也支持载荷:
'decrement' // 将 `this.decrement(amount)` 映射为 `this.$store.dispatch('decrement', amount)`
]),
...mapActions({
add: 'increment' // 将 `this.add()` 映射为 `this.$store.dispatch('increment')`
})
}
}
3. 组合 Action
action 通常是异步的,通过组合多个 action,以处理更加复杂的异步流程。
首先, store.dispatch 可以处理被触发的 action 的处理函数返回的 Promise,并且 store.dispatch 仍旧返回 Promise:
actions: {
actionA ({ commit }) {
return new Promise((resolve, reject) => {
setTimeout(() => {
commit('someMutation')
resolve()
}, 1000)
})
}
}
现在你可以:
store.dispatch('actionA').then(() => {
// ...
})
在另外一个 action 中也可以:
actions: {
// ...
actionB ({ dispatch, commit }) {
return dispatch('actionA').then(() => {
commit('someOtherMutation')
})
}
}
最后,如果我们利用 async / await (opens new window),我们可以如下组合 action:
// 假设 getData() 和 getOtherData() 返回的是 Promise
actions: {
async actionA ({ commit }) {
commit('gotData', await getData())
},
async actionB ({ dispatch, commit }) {
await dispatch('actionA') // 等待 actionA 完成
commit('gotOtherData', await getOtherData())
}
}
一个 store.dispatch 在不同模块中可以触发多个 action 函数。在这种情况下,只有当所有触发函数完成后,返回的 Promise 才会执行。
六、Module
由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。
为了解决以上问题,Vuex 允许我们将 store 分割成模块(module),每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块(modules)。
作用是 模块化管理,处理各自的业务逻辑。
1. 使用
(1)首先,创建a.js 和 b.js;
a.js:
export default {
state: () => ({
count: 1
}),
getters: { ... }
mutations: { ... },
actions: { ... }
}
b.js:
export default {
state: () => ({
count: 2
}),
getters: { ... }
mutations: { ... },
actions: { ... }
}
(2)然后,在 store.js 中引入 a.js 和 b.js:
import moduleA from './module/a'
import moduleB from './module/b'
export default new Vuex.Store({
state: 0,
...
modules: {
a: moduleA,
b: moduleB
}
})
(3)在组件中获取 state 中的 count :
this.$store.state.count // -> 全局store 中的count: 0
this.$store.state.a.count // -> moduleA 中的count: 1
this.$store.state.b.count // -> moduleB 中的count: 2
(4)在组件中使用 mutations:
this.$store.commit('increment')
会发现触发了全局store,a,b 中三个全部的mutations,如果只想触发 moduleA 中的 increment,需要引入命名空间的概念。
2. 命名空间
默认情况下,模块内部的 action、mutation 和 getter 是注册在全局命名空间的——这样使得多个模块能够对同一 mutation 或 action 作出响应。
如果希望你的模块具有更高的封装度和复用性,可以通过添加 namespaced: true 的方式使其成为带命名空间的模块,再通过模块路径去触发。
这样当模块被注册后,它的所有 getter、action 及 mutation 都会自动根据模块注册的路径调整命名。
a.js:
export default {
// 开启命名空间
namespaced: true,
state: () => ({
count: 1
}),
getters: { ... }
mutations: { ... },
actions: { ... }
}
在组件中去触发 a 中的 increment 方法:
this.$store.commit('a/increment')
在组件中去触发 b 中的 increment 方法:
this.$store.commit('b/increment')
同理 getters,actions 提交 mutations 也需要添加路径:
this.$store.getters('a/getOdd')
this.$store.dispatch('a/increment')
2.1 在带命名空间的模块内访问全局内容
rootState 和 rootGetters 作为参数传入,即可访问全局 store 中的 state 和 getters 。
a.js:
export default {
namespaced: true,
state: () => ({
count: 1,
arr: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
}),
getters: {
getOdd (state) {
return state.arr.filter(item => {
return item % 2 === 0
})
},
getOddLength (state, getters) {
return getters.getOdd.length
},
// 第三/四个参数:rootState / rootGetters
getOddTotalSum (state, getters, rootState, rootGetters) {
return getters.getOddLength + rootState.arr.length
}
},
mutations: {
increment (state, payload) {
state.count += payload.amount
}
},
actions: {
// 传入 rootState , rootGetters
increment ({commit, rootState, getters, rootGetters}, payload) {
commit('increment', payload)
// console.log(rootState.count) -> 0
// console.log(getters.getOdd) -> 'a/getOdd'
// console.log(rootGetters.getOdd) // -> 'getOdd'
}
}
}
2.2 在带命名空间的模块注册全局 action
若需要在带命名空间的模块注册全局 action,可添加 root: true,并将这个 action 的定义放在函数 handler 中。
a.js:
actions: {
// 之前的 increment 是函数形式,现在是对象形式
increment: {
// 全局注册
root: true,
// handler => 之前的 increment
handler ({commit}, payload) {
commit('increment', payload)
}
}
}
vuex-store.js:
actions: {
// 模块中的increment不可与全局store中的increment重名
// 所以我们给它命名为 `root_increment`
root_increment ({dispatch}, payload) {
// 调用在a.js中全局注册的 increment action
dispatch('increment', payload)
}
}
在组件中调用 root_increment:
this.$store.dispatch('root_increment', {
amount: 1
})
2.3 在组件中与 mapState, mapGetters, mapActions 和 mapMutations 连用
vuex-store.js:
<template>
<div>
<div>全局store:{{ count }}</div>
<div>a:{{ count_a }}</div>
<div>b:{{ count_b }}</div>
<button @click="add({amount: 1})">+1</button>
<button @click="decrement({amount: 1})">-1</button>
</div>
</template>
<script>
import {mapState, mapGetters, mapMutations, mapActions} from 'vuex'
export default {
name: 'HelloModule',
computed: {
...mapState({
count: state => state.count,
count_a: state => state.a.count,
count_b: state => state.b.count
}),
...mapGetters({
getOddTotalSum: 'a/getOddTotalSum'
})
},
methods: {
...mapMutations({
add: 'a/increment'
}),
...mapActions([
'decrement'
])
},
created () {
console.log(this.getOddTotalSum)
}
}
</script>
3. 模块动态注册
3.1 注册模块
在 store 创建之后,可以使用 store.registerModule 方法注册模块。
vuex-store.js:
import Vuex from 'vuex'
const store = new Vuex.Store({ /* 选项 */ })
// 动态注册模块 `c`
store.registerModule('c', {
state: {
count: 4
},
mutations: {
increment (state) {
state.count++
}
}
})
export default store
在组件中访问 c 模块下的count变量:
console.log(this.$store.state.c.count); // 4
3.2 注册嵌套模块
注册嵌套模块, store.registerModule 方法第一个参数传入数组。
vuex-store.js:
// 注册嵌套模块 `c/myModule`
store.registerModule(['c', 'myModule'], {
// ...
})
// 注册嵌套模块 `c/myModule/nested`
store.registerModule(['c', 'myModule','nested'], {
// ...
})
在组件中访问嵌套模块下的count变量:
console.log(this.$store.state.c.myModule.count);
console.log(this.$store.state.c.myModule.nested.count);
模块动态注册功能使得其他 Vue 插件可以通过在 store 中附加新模块的方式来使用 Vuex 管理状态。例如,vuex-router-sync 插件就是通过动态注册模块将 vue-router 和 vuex 结合在一起,实现应用的路由状态管理。
3.2 卸载模块
在动态创建模块之后,可以使用 store.unregisterModule(path) 方法来动态卸载模块。
vuex-store.js:
// 卸载模块 `c`
store.unregisterModule('c')
// 卸载嵌套模块 `c/myModule/nested`
store.unregisterModule(['c', 'myModule','nested'])
注意:
- 参数 path 可以是字符串,也可以是数组;
- store.unregisterModule() 只能卸载动态注册的模块,不可卸载store中声明的静态模块(即创建 store 时声明的模块,例如:模块a,b)。
3.3 判断模块是否被注册到store
可以通过 store.hasModule(path) 方法检查该模块是否已经被注册到 store。
vuex-store.js:
// 检查是否注册模块 `c`
console.log(store.hasModule('c')); // true
// 检查是否注册嵌套模块 `c/myModule/nested`
console.log(store.hasModule(['c', 'myModule','nested'])); // true
3.4 保留 state
当设置 preserveState: true 时,嵌套模块会保留上一级模块的state。
vuex-store.js:
store.registerModule('c', {
preserveState: true
})
当设置 preserveState: true 时,该模块会被注册,action、mutation 和 getter 会被添加到 store 中,但是 state 不会。这里假设 store 的 state 已经包含了这个 module 的 state 并且你不希望将其覆写。
4. 带命名空间的绑定函数
当有许多嵌套模块时,mapState, mapGetters, mapActions 和 mapMutations 写起来会比较繁琐:
vuex-store.js:
store.registerModule(['c', 'myModule', 'nested'], {
namespaced: true,
state: {
count: 1
},
mutations: {
increment (state) {
state.count++
}
},
actions: {
increment ({commit}) {
commit('increment')
}
}
})
组件中:
computed: {
...mapState({
nested: state => state.c.myModule.nested.count
}),
methods: {
...mapActions({
nestedAdd: 'c/myModule/nested/increment'
})
}
4.1 使用模块的空间名称字符串
对于上述情况,我们可以简化为:
组件中:
computed: {
...mapState('c/myModule/nested',{
nested: state => state.count
}),
methods: {
...mapActions('c/myModule/nested', {
nestedAdd: 'increment'
})
}
将模块 路径 作为第一个参数传递给上述函数,这样所有绑定都会自动将该模块作为上下文。
4.2 使用createNamespacedHelpers
可以通过使用 createNamespacedHelpers 创建基于某个命名空间辅助函数。它返回一个对象,对象里有新的绑定在给定命名空间值上的组件绑定辅助函数。
组件中:
// 1. 引入 createNamespacedHelpers
import { createNamespacedHelpers } from 'vuex'
// 2. 创建辅助函数
// 注释之前的引入代码
// import {mapState, mapActions} from 'vuex'
const {mapState, mapActions} = createNamespacedHelpers('c/myModule/nested')
// 3. 使用
export default {
computed: {
...mapState({
nested: state => state.count
})
},
methods: {
...mapActions([
'increment'
])
}
}
5. 模块重用
有时我们可能需要创建一个模块的多个实例。
如果我们使用一个纯对象来声明模块的状态,那么这个状态对象会通过引用被共享,导致状态对象被修改时 store 或模块间数据互相污染的问题。
实际上这和 Vue 组件内的 data 是同样的问题。因此解决办法也是相同的——使用一个函数来声明模块状态(仅 2.3.0+ 支持):
const myModule = {
state: () => ({
count: 1
}),
// mutation, action 和 getter 等等...
}
七. 总结
辅助函数的位置:
- mapState, mapGetters :写在computed中;
- mapMutations, mapActions :写在methods中。
代表的意义:
- state: 变量;
- getters: state的计算属性;
- mutations: 同步;
- actions: 异步。
Getters中的参数:
- state: 如果在模块中定义,则为局部的state;
- getters: 如果在模块中定义,则为局部的getters;
- rootState: 相当于全局的state,即store.state;
- rootGetters: 相当于全局的getters,即store.getters。
八. 项目结构
Vuex 并不限制你的代码结构。但是,它规定了一些需要遵守的规则:
- 应用层级的状态应该集中到单个 store 对象中。
- 提交 mutation 是更改状态的唯一方法,并且这个过程是同步的。
- 异步逻辑都应该封装到 action 里面。
只要你遵守以上规则,如何组织代码随你便。如果你的 store 文件太大,只需将 action、mutation 和 getter 分割到单独的文件。
对于大型应用,我们会希望把 Vuex 相关代码分割到模块中。下面是项目结构示例:
├── index.html
├── main.js
├── api
│ └── ... # 抽取出API请求
├── components
│ ├── App.vue
│ └── ...
└── store
├── index.js # 我们组装模块并导出 store 的地方
├── actions.js # 根级别的 action
├── mutations.js # 根级别的 mutation
└── modules
├── cart.js # 购物车模块
└── products.js # 产品模块
官网链接: Vuex