本文章节如下
- 首先先来看看
Vuex
的整体流程 - 然后介绍一些比较常见的
API
的用法 - 最后则是介绍一下初始化装载与注入的过程
至于源码的部分,则是会下一篇章节当中来进行介绍
流程图
Vuex
的整个执行流程如下(转自网络)
Vuex
为 Vue Components
建立起了一个完整的生态圈,包括开发中的 API
调用一环,围绕这个生态圈,简要介绍一下各模块在核心流程中的主要功能
Vue Components
Vue
组件,HTML
页面上负责接收用户操作等交互行为- 执行
dispatch
方法触发对应action
进行回应
dispatch
- 操作行为触发方法,是唯一能执行
action
的方法
- 操作行为触发方法,是唯一能执行
actions
- 操作行为处理模块,负责处理
Vue Components
接收到的所有交互行为,包含同步或者异步的操作 - 支持多个同名方法,按照注册的顺序依次触发,向后台
API
请求的操作就在这个模块中进行,包括触发其他action
以及提交mutation
的操作 - 该模块提供了
Promise
的封装,以支持action
的链式触发
- 操作行为处理模块,负责处理
commit
- 状态改变提交操作方法,对
mutation
进行提交 - 是唯一能执行
mutation
的方法
- 状态改变提交操作方法,对
mutations
- 状态改变操作方法,是
Vuex
修改state
的唯一推荐方法,其他修改方式在严格模式下将会报错 - 该方法只能进行同步操作,且方法名只能全局唯一,操作之中会有一些
Hook
暴露出来,以进行state
的监控等
- 状态改变操作方法,是
state
- 页面状态管理容器对象,集中存储
Vue components
中data
对象的零散数据 - 全局唯一,以进行统一的状态管理,页面显示所需的数据从该对象中进行读取,利用
Vue
的细粒度数据响应机制来进行高效的状态更新
- 页面状态管理容器对象,集中存储
getters
state
对象读取方法,图中没有单独列出该模块,应该被包含在了render
中Vue Components
通过该方法读取全局state
对象
总结如下
Vue
组件接收交互行为,调用dispatch
方法触发action
相关处理- 若页面状态需要改变,则调用
commit
方法提交mutation
修改state
- 通过
getters
获取到state
新值,重新渲染Vue Components
,界面随之更新
目录结构
目录 | 介绍 |
---|---|
module |
提供 module 对象与 module 对象树的创建功能 |
plugins |
提供开发辅助插件,如时光穿梭功能,state 修改的日志记录功能等 |
helpers.js |
提供 action 、mutations 以及 getters 的查找 API |
index.js |
是源码主入口文件,提供 store 的各 module 构建安装 |
mixin.js |
提供了 store 在 Vue 实例上的装载注入 |
util.js |
提供了工具方法如 find 、deepCopy 、forEachValue 以及 assert 等方法 |
下面我们就来看看其中的一些 API
的用法,比如之前介绍过的 State
,Getter
,Mutation
和 action
等,Vuex
使用单一状态树,每个应用将仅仅包含一个 store
实例,从 store
实例中读取状态最简单的方式就是在计算属性当中返回某个状态
State
Vuex
通过 store
选中,将状态从根组件注入到每一个子组件当中
1 | new Vue({ |
子组件可以通过 this.$store
访问到该 store
的实例
1 | const Counter = { |
Getter
简单的来说,可以将其理解为 store
的计算属性,getter
的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算,它接收 state
作为『第一个参数』
1 | const store = new Vuex.store({ |
外部调用
1 | store.getters.doneTodos // [{ id: 1, text: '...', done: true }] |
也可以接受其他的 getter
作为『第二个参数』
1 | getters: { |
也可以返回一个函数,来实现给 getter
传参
1 | getters: { |
Mutation
更改 Vuex
的 store
中的状态的唯一方法就是提交 mutation
,非常类似于事件
- 每个
mutation
都有一个事件类型(type
)和一个回调函数(handler
) - 回调函数就是进行状态更改的地方,并且接收
state
作为第一个参数
1 | const store = new Vuex.Store({ |
需要注意
- 不能直接调用
mutation handle
- 应当以相应的
type
调用store.commit
方法
1 | store.commit('increment') |
同时可以向 store.commit
传入额外的参数,即 mutation
的载荷(payload
)
1 | // ... |
Mutation 的提交
- 必须是同步函数(若是异步,则可能存在当
mutation
触发的时候,回调函数还没有被调用的情况) mutation
的提交可以使用this.$store.commit('...')
- 或者可以使用
mapMutations
辅助函数将组建中的methods
映射为store.commit
调用,如下所示
1 | import { mapMutations } from 'Vuex'; |
Action
- 提交的是
mutation
,而不是直接变更状态 - 可以包含任意异步操作
一个简单的 action
1 | const store = new Vuex.store({ |
action
函数接受一个与 store
实例具有相同方法和属性的 Context
对象,因此可以调用 context.commit
提交一个 mutation
分发 Action
action
通过 store.dispatch
方法触发
1 | store.dispatch('increment') |
之所以这样使用,是因为 mutation
必须同步执行,而 action
则不必如此,可以在其内部执行异步操作
1 | actions: { |
组件当中 Action 的分发
同 Mutation
类似,可以使用 this.$store.dispatch('...')
或者使用 mapActions
辅助函数
组合 Action
因为 store.dispatch
返回的是一个 Promise
对象,所以可以使用 then()
方法来进行处理,亦或是可以使用 async/await
1 | // 假设 gotData() 与 gotOtherData() 均返回 Promise |
初始化装载与注入
下面我们就来看看 Vuex
到底是如何在项目当中进行装载与注入的,首先是入口文件,先来看入口处的 export
函数到底导出了哪些东西,详细可以见官方 vuejs/vuex
需要注意的是,可能版本不同而导致内容有所不同,但是我们关心的仅仅是几个核心方法
1 | // https://github.com/vuejs/vuex/blob/dev/src/index.js |
装载与注入
我们一般在使用 Vuex
的时候如下所示
1 | // store.js |
除了 Vue
的初始化代码,只是多了一个 store
对象的传入,我们来看下源码中的实现方式
1 | // store.js |
若是首次加载,将局部 Vue
变量赋值为全局的 Vue
对象,并执行 applyMixin
方法
1 | // store.js |
下面是 applyMixin
的源码,如果是 2.x
以上的版本,可以使用 Hook
的形式进行注入,即在 beforeCreated
钩子前插入初始化代码(vuexInit
)
1 | export default function (Vue) { |
这也就是为什么我们在 Vue
的组件中可以通过 this.$store.xxx
来访问到 Vuex
的各种数据和状态的原因了,因为在任意组件中执行 this.$store
都能找到装载的那个 store
对象,如下图所示,页面的结构为
对应的 store
流向