VUEX 3 源码分析——4. 理解Action
好久不见了小伙伴们,今天继续发我的Vuex源码分析。
这段时间我现在已经做完了vuex和vue-router的源码分析笔记,不过还是按功能一节一节地来发吧。
另外我现在还在学习鱼皮大佬的《伙伴匹配系统》项目(但是后端使用Python-Flask实现),里面的一些知识狠狠地弥补了我技术上的不足(主要是因为我的工作是重业务的,反而偏基础的一些东西不是很熟)。
活不多说,开始理解vuex的action吧!
官网示例
-
先看一下官网示例:
-
在actions上定义函数,函数接受一个context对象,这个对象可以调用commit,state和getters。(官方说context对象不是store实例本身,等会看内部的实现源码就能更直观地感受到了)
-
action通过dispatch方法触发
-
辅助函数 mapActions
-
如果你看过我之前分析的两篇文章(Getter和Mutation),就能发现action的功能和getter,尤其是和mutation大同小异,只是action能进行异步操作嘛。那JavaScript里面,哪种语法适合异步操作能——当然是Promise啦。
-
所以通过深入分析开源项目中的特定功能,我们可以洞察到项目背后的统一设计思想。
-
理解这一设计思想不仅有助于我们掌握单个功能的具体实现,还能帮助我们预测和理解其他功能的设计模式。
-
此外,阅读和学习优秀的开源项目,能够显著提升我们的代码理解能力和审美水平,从而在软件开发实践中,更好地应用这些设计原则和模式。
const store = new Vuex.Store({ state: { count: 0 }, mutations: { increment (state) { state.count++ } }, actions: { increment (context) { context.commit('increment') } } }) store.dispatch('increment')
初始化Actions
-
之前的Getter和Mutation初始化工作,都是在installModule函数中执行的,action也必然在此。
-
Store类初始化一个无原型链对象 this_actions:this._actions = Object.create(null)
-
将用户定义的action按key-value格式,存储进 this._actions[key] 中,其中的每个key对应一个数组,数组中的元素为函数,每个函数都返回一个执行action的Promise
// store-util.js export function installModule(store, rootState, path, module, hot) { ... module.forEachAction((action, key) => { const type = action.root ? key : namespace + key; const handler = action.handler || action; registerAction(store, type, handler, local) }) } function registerAction(store, type, handler, local) { const entry = store._actions[type] || (store._actions[type] = []); entry.push(function wrappedActionHandler(payload) { const store_obj = { dispatch: local.dispatch, commit: local.commit, getters: local.getters, state: local.state, rootGetters: store.getters, rootSatte: store.state } // 这就是官方手册说的action函数接受的context对象不是store实例本身 let res = handler.call(store, store_obj, payload) // 如果res不是promise,将被封装在一个Promise对象中 if (!isPromise(res)) { res = Promise.resolve(res) } return res }) } // ./module/module.js export default class Module { ... forEachAction(fn) { if (this._rawModule.actions) { forEachValue(this._rawModule.actions, fn) } } }
实现dispatch方法
- 在Store类上定义dispatch方法。将传入的参数格式化后,以Promise.all的方式调用 this._actions[type] 中的所有promise,返回一个新的Promise。
// ./store.js export class Store { ... dispatch(_type, _payload) { // unifyObjectStyle函数让 Actions 支持同样的载荷方式和对象方式进行分发 const {type, payload} = unifyObjectStyle(_type, _payload) const action = {type, payload} // 分发 sub action 用的 ... const result = entry.length > 1 ? Promise.all(entry.map(handler => handler(payload))) : entry[0](payload) return new Promise((resolve, reject) => { result.then(res => { ... // 插件的处理 resolve(res) }, error => { ... // 插件的处理 reject(error) }) }) } } // ./store-util.js export function unifyObjectStyle (type, payload, options) { if (isObject(type) && type.type) { options = payload payload = type type = type.type } return { type, payload, options } }
-
最后将dispatch方法以类似隔离的方式,绑定store为 dispatch 的指定上下文。
-
确保在不同的执行环境中调用dispatch时,它总是与创建它的store实例相关联。
// ./store.js export class Store { const store = this; const { dispatch, commit } = this; this.dispatch = function boundDispatch(type, payload) { return dispatch.call(store, type, payload) } }
mapActions 辅助函数
- 实现方式和mapMutations一模一样,只是将commit换成了dispatch
// ./helper.js export const mapActions = normalizeNamespace((namespace, actions) => { const res = {} ... normalizeMap(actions).forEach(({ key, val }) => { res[key] = function mappedAction (...args) { // get dispatch function from store let dispatch = this.$store.dispatch ... return typeof val === 'function' ? val.apply(this, [dispatch].concat(args)) : dispatch.apply(this.$store, [val].concat(args)) } }) return res })
以上就是官网上Action的相关源码,下一遍会分析Module的实现源码。
