Vuex是一个专为Vue.js应用程序开发的状态管理模式。
解决问题:
1、多个视图依赖于同一状态。
2、来自不同视图的行为需要变更同一状态。
工作原理图
组件内
store.dispatch(‘actionsName’)一个actions->actions内context.commit(‘mutationName’)一个mutations->mutations内 直接改变state
不使用actions(不包含异步操作):组件内store.commit(‘mutationName’)一个mutations->mutations内 直接改变state

安装
1 | npm install vuex --save |
兼容性
Vuex 依赖 Promise。如果你支持的浏览器并没有实现 Promise (比如 IE),那么你可以使用一个 polyfill 的库,例如 es6-promise。
然后在 使用 Vuex 前面引入
1 | npm install es6-promise --save |
与全局变量的区别
1、Vuex 的状态存储是响应式的
当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到更新。
2、不能直接改变 store 中的状态
改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation,这样使得我们可以方便地跟踪每一个状态的变化。
规律
1、state(mapState)、 getter(mapGetters) 对应 computed计算属性
2、mutations(mapMutations) 、 actions(mapActions)对应methods
3、state是普通对象(json)
getter每一项是: (属性名:属性值) 属性值为一个有返回值的函数
mutations、actions每一项都是一个函数(可以使用es6 的函数缩写xxx(state) {xxx})
4、getter函数
接受
state作为第一个参数接受
其他getter作为第二个参数通过让
Getter函数返回一个函数,来实现给getter传参
5、mutations函数
接受
state作为第一个参数接收
payload(实参、有效荷载)作为第二个参数
6、actions函数
context作为第一个参数,调用context.commit提交一个mutation,或者通过context.state和context.getters来获取state和getters
payload(实参、有效荷载)作为第二个参数
7、语法糖第一个参数是可选的,可以是一个命名空间字符串
8、语法糖第二个参数一般传入json,每一个属性名对应的属性值只能是普通函数、箭头函数、字符串
8.1、使用
常规函数,才能使用this获取所在组件的状态8.2、使用
箭头函数,不能使用this获取所在组件的状态,可使代码更简练接收state作为第一个参数获取到store的所有数据8.3、使用字符串,
countAlias: 'count';countAlias相当于给count起的别名;'count'相当于state => state.count8.4、语法糖
第二个参数的每一项,可以是属性名:属性值, 也可以是一个有返回值的函数xxx(state) {return xxx}
9、语法糖第二个参数也可以是一个数组:当映射的计算属性的名称与 state 的子节点名称相同时
10、除了语法糖引入mutations和actions直接调用以外,mutations对应context.commit('xxx')或store.commit('xxx'),actions对应store.dispatch('xxx')
简单的 使用
如果在模块化构建系统中,请确保在开头调用了
Vue.use(Vuex)通过
store.state来获取状态对象通过
store.commit方法触发mutations进行状态变更
1 | Vue.use(Vuex) |
state (状态、数据)
因为
store中的state是响应式的所以利用computed计算属性进行读取state
子组件能通过 this.$store访问到store
在模块化的构建系统中,从根组件“注入”到每一个子组件中(需调用
Vue.use(Vuex)之后将store挂载到vue上),子组件能通过this.$store访问到
1 | import Vue from 'vue'; |
mapState辅助函数(语法糖)必须return
当使用
普通函数与箭头函数时接收state作为第一个参数为了能够使用
this获取组件局部状态,必须使用常规函数
1 | //计算属性只从vuex继承 |
getter
1、从
store中的state中派生出的一些状态,获取并处理state中的数据以生成新的数据2、一般都是通过
getter获取state中的数据(不管数据是否需要处理)3、通过
store.getters对象以属性的形式访问getter中的选项store.getters.doneTodos
Getter函数接受state作为第一个参数(数据来源)
1 | const store = new Vuex.Store({ |
Getter函数接受其他 getter 作为第二个参数,用来访问其他getter选项
1 | getters: { |
通过让 Getter函数返回一个函数,来实现给 getter 传参
1 | getters: { |
在子组件的computed中通过this.$store.getters获取getter中的属性
1 | computed: { |
mapGetters 辅助函数
mapGetters辅助函数仅仅是将store中的getter映射到局部计算属性
1 | import { mapGetters } from 'vuex' |
如果你想将一个 getter 属性另取一个名字,使用对象形式mapGetters({doneCount: ‘doneTodosCount’})
1 | mapGetters({ |
Mutation(突变):
更改
Vuex的store中的状态的唯一方法是提交mutation
类似于事件:每个mutation都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler)。这个回调函数就是我们实际进行状态更改的地方
接受 state 作为第一个参数:
1 | const store = new Vuex.Store({ |
使用store.commit调用mutation函数
不能直接调用一个
mutation handler(处理程序)要唤醒一个
mutation handler,需要以相应的type调用store.commit方法:
1 | store.commit('increment') |
向store.commit中传递参数:store.commit(‘increment’, {})
可以向
store.commit传入额外的参数:载荷(Payload)在大多数情况下,载荷应该
是一个对象,这样可以包含多个字段并且记录的mutation会更易读
1 | //基本数据类型 |
Mutation 需遵守 Vue 的响应规则
最好提前在
store中初始化好所有所需属性当需要在对象上添加新属性时,你应该
(1) 使用
Vue.set(obj, 'newProp', 123), 或者(2)以新对象替换老对象。例如,利用 对象展开运算符:
state.obj = { ...state.obj, newProp: 123 }
使用常量替代 Mutation 事件类型(Mutation Type)
使用常量替代
mutation事件类型,同时把这些常量放在单独的文件中
1 | // mutation-types.js |
Mutation 必须是同步函数(不能在里边用定时器及调用接口)
在组件中提交 Mutation
- 在组件中使用
this.$store.commit('xxx')提交mutation - 使用
mapMutations辅助函数将组件中的methods映射为store.commit调用(需要在根节点注入 store)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15import { mapMutations } from 'vuex'
export default {
// ...
methods: {
...mapMutations([
'increment', // 将 `this.increment()` 映射为 `this.$store.commit('increment')`
// `mapMutations` 也支持载荷:
'incrementBy' // 将 `this.incrementBy(amount)` 映射为 `this.$store.commit('incrementBy', amount)`
]),
...mapMutations({
add: 'increment' // 将 `this.add()` 映射为 `this.$store.commit('increment')`
})
}
}
Action(行动)
Action 类似于 mutation不同在于:
Action提交的是mutation,而不是直接变更状态Action可以包含任意异步操作
Action 函数接受一个与 store 实例具有相同方法和属性的 context 对象(上下文对象)作为第一个参数
可以调用
context.commit提交一个mutation通过
context.state和context.getters来获取state和getters
context 对象不是 store 实例本身了
payload (实参、有效荷载)作为第二个参数
代码示例:
1 | const store = new Vuex.Store({ |
分发 Action
Action通过store.dispatch方法触发store.dispatch('increment')
Actions支持同样的载荷方式和对象方式进行分发store.dispatch('incrementAsync', {})一个
Actions可以派发另一个Actions
1 | // 以载荷形式分发 |
购物车实例
1 | actions: { |
在组件中分发 Action
在组件中使用
this.$store.dispatch('xxx')分发action使用
mapActions辅助函数将组件的methods映射为store.dispatch调用(需要先在根节点注入store)
1 | import { mapActions } from 'vuex' |
Action 最好通过返回 Promise或者async await 实现异步编程 并 组合 Action
store.dispatch可以处理被触发的action的处理函数返回的Promise,并且store.dispatch仍旧返回Promise
1 | actions: { |
Module
将
store分割成模块(module)。每个模块拥有自己的state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割
模块内部的
mutation和getter,接收的第一个参数是模块的局部状态对象state模块内部的
getter,第二个参数是其他getter模块内部的
getter,第三个参数(rootState)是根节点状态(根节点state)模块内部的
action,局部状态通过context.state暴露出来,根节点状态则为context.rootState模块中的
state会集成到根节点的state,(根节点store).state.(模块名)模块内部的
action、mutation 和 getter是注册在全局命名空间的——这样使得多个模块能够对同一mutation或action作出响应,可以像调用根节点的action、mutation 和 getter一样直接调用,也可以 通过namespace用路径访问
1 | const moduleA = { |
组件中获取module中的state数据:this.$store.state.命名空间名称
模块内的状态已经是嵌套的了,使用
namespaced(命名空间)进行获取,只能使用下面方法获取
1 | computed:{ |
组件中获取module中的getter数据:this.$store.getters.aaa
因为模块内部的
getter、mutation 和 action是注册在全局命名空间的 ,不开起namespaced情况下直接获取时不用写model名
1 | computed:{ |
命名空间
模块内部的
action、mutation 和 getter是注册在全局命名空间的,这样使得多个模块能够对同一mutation或action作出响应意思就是:在模块不开起
namespaced: true的情况下,调用model中的action、mutation和getter就跟调用根节点的action、mutation 和 getter是一样的
不开启namespaced的 示例:
1 | //store.js |
开启namespaced(强制getter、mutation、action使用命名空间)
在模块多级嵌套中:
不开启namespaced的模块的getter、action 及 mutation会自动挂在在父模块的命名空间1、
开启namespaced的模块会进一步嵌套命名空间2、通过添加
namespaced: true的方式使其成为带命名空间的模块3、当模块被注册后,它的所有
getter、action 及 mutation都会自动根据模块注册的路径调整命名模块内的状态已经是嵌套的了,使用
namespaced属性不会对其产生影响
1 | const store = new Vuex.Store({ |
启用了命名空间的getter 和 action会收到局部化的(模块内部的)getter,dispatch 和 commit;在
模块内部(在store.js不是在vue组件内)访问模块自己的getter,dispatch 和 commit,不用加路径
在带命名空间的模块内访问全局内容
1、带命名空间的模块内的
getter,要使用全局state和getter,需要将rootState和rootGetter会作为第三和第四参数传入模块内的getter2、带命名空间的模块内的
action,要使用全局state和getter,需要通过context对象获取,context.rootState 和 context.rootGetters3、若需要在模块内部 分发
全局命名空间内action或提交全局命名空间内mutation,将{ root: true }作为第三参数传给dispatch或commit即可commit('someMutation', null, { root: true })
1 | modules: { |
在带命名空间的模块注册全局
若需要在带命名空间的模块注册全局
action,可以添加root: true,并将这个action的定义放在函数handler中
1 | { |
带命名空间的绑定函数(语法糖)
mapState, mapGetters, mapActions 和 mapMutations这些函数来绑定带命名空间的模块时,写起来可能比较繁琐
1 | computed: { |
1、可以将模块的空间名称字符串作为第一个参数传递给上述函数,这样所有绑定都会自动将该模块作为上下文mapState(‘some/nested/module’, {})
上面的例子可以简化为:
1 | computed: { |
2、可以通过使用 createNamespacedHelpers 创建基于某个命名空间辅助函数
它返回一个对象,对象里有新的绑定在给定命名空间值上的组件绑定辅助函数:
1 | //1 |
模块动态注册store.registerModule
使用
store.registerModule方法注册模块:
1 | // 注册模块 `myModule` |
之后就可以通过
store.state.myModule和store.state.nested.myModule访问模块的状态
可以使用 store.unregisterModule(moduleName) 来动态卸载模块。注意,你不能使用此方法卸载静态模块(即创建 store 时声明的模块)
在注册一个新
module时,你很有可能想保留过去的state,例如从一个服务端渲染的应用保留state。你可以通过preserveState选项将其归档:store.registerModule('a', module, { preserveState: true })。
模块重用
起因:
创建多个 store,他们公用同一个模块,
在一个 store 中多次注册同一个模块
问题:如果使用一个纯对象来声明模块的状态,因为这个状态对象是引用数据类型当被多处引用时,一个模块改变其他的模块也跟着变,导致状态对象被修改时 store 或模块间数据互相污染的问题。
解决办法:利用函数每次调用互不影响的特性,返回一个对象
1 | const MyReusableModule = { |
插件
Vuex插件就是一个函数,它接收store作为唯一参数在插件中不允许直接修改状态——类似于组件,只能通过提交
mutation来触发变化
1 | //创建 |
内置 Logger 插件
日志插件
1 | import createLogger from 'vuex/dist/logger' |
createLogger 函数的配置项:
1 | const logger = createLogger({ |
表单处理( 双向数据绑定问题)
由于这个修改不是在 mutation 函数中执行的, 这里会抛出一个错误。
解决方法:给 <input> 中绑定 value,然后侦听 input 或者 change 事件,在事件回调中调用 action
1 | //方法1 |
使用带有 setter 的双向绑定计算属性
1 | //方法2 使用带有 setter 的双向绑定计算属性 |
项目结构

index.js
1 | import Vue from 'vue'; |
state.js
1 | //状态树 |
getter.js
1 | //从这里取state取数据 到组件中 |
mutation-types.js
1 | //一些字符串常量 |
mutation.js
1 | //定义修改的操作 修改state中的数据 |
actions.js
1 | //异步操作 更改store的状态 |