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.count
8.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
会作为第三和第四参数
传入模块内的getter
2、带命名空间的模块内的
action
,要使用全局state
和getter
,需要通过context
对象获取,context.rootState 和 context.rootGetters
3、若需要在模块内部 分发
全局命名空间内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的状态 |