前置篇不会那可不行!Vuex 源码学习(六)action 和 mutation 如何被调用的(前置准备篇)在前置准备篇我们已经知道被处理好的 action 与 mutation 都被集中放置在哪里了。下面就要看 dispacth 和 commit 如何去调用它们。
dispatch 与 commit 的实现
commit:
首先呢我们要校正参数,把传入的参数整理下主要是处理这种形式
// 接收一个对象
this.$store.commit({type : ‘setName’,name : ‘xLemon’});
this.$store.commit(‘setName’,{name : ‘xLemon’});
这两个基本等价。
只是第一种方式 mutation 接收的 payload 会比第二种多一个 type 属性,
整理的部分并不关键
type 是我们要找的 mutation 的名字,如何找到 mutation 呢?
通过 this._mutations[type] 找到要执行的 mutation
所以 type 一定要是 mutation 的全名
所以我们通过 commit 找 mutation 的时候有命名空间的时候就要输入全名,(那种带很多 / 的)。没有这个名字的 mutation 容错处理,然后在 withCommit 函数的包裹下,完成了 mutation 的执行(所有 mutation 啊,全名相同的 mutation 都会被执行)。然后呢遍历_subscribers 里面的函数进行执行。
_subscribers 这是什么?在一开始我们可以注册一些内容(函数),在 commit 完成时被通知执行。(观察者模式)如何注册在这一章就不多讲了!后面章节会统一讲述。
这就是 commit 做的事情。
dispatch 呢?
与 commit 大同小异,也有一个_actionSubscribers 的属性,在 dispatch 执行之前全部调用。对于 dispatch Vuex 推荐的是放置异步任务,在注册 action 的时候已经被强制 promise 化了,所以有多个同名 action 会通过 Promise.all 来处理。在 action 的前后都有对应的钩子函数执行。
固定 disptach 与 commit 的 this 指向
// 在 vue 组件内一个方法如果多次使用 dispatch 和 commit,就会很麻烦。
this.$store.dispatch(‘xxx’,payload);
this.$store.commit(‘xxx’,payload);
const {dispatch,commit} = this.$store;
// 这相当于创建变量,然后把 this.$store 的 dispatch 与 commit 赋值给它们。
// 有经验的都应该知道,函数 dispatch 和 commit 的 this 指向在严格模式下指向 undefined。
// 非严格模式下指向 window,
// 刚才的源码中我们也看到了,dispatch 和 commit 都依赖于 Store 实例。怎么办??
解决方法如下:dispatch 和 commit 是 Store 原型链上的方法,在 constructor 内注册了构造函数内的方法,把原型上的 dispatch 和 commit 进行了一个 this 指向的强制绑定,通过 call 让两个方法永远指向 Store 的实例上,保证了 dispatch 和 commit 不会因为赋值给其余变量等操作导致 this 指向改变从而发生问题
action 与 mutation 函数的参数都有哪些?怎么来的?
看一个简单的 mutation:
export const setName = function(state,payload) {
state.name = payload;
};
这个时候不经意间有了一个疑惑?state 哪里来的。这就要从 mutation 被注册的函数内找原因了
handle 是我们要被注册的一个 mutation,entry 是这个同名 mutation 的容器(存储所有的这个名字的 mutation,一般只有一个)在吧 handle 放入 entry 的过程中,
我们发现,entry 是被一个函数包裹起来,然后将 local.store 和 payload 绑定到这个 handle 的参数上,然后把 handle 的 this 指向锁定到了 Store 实例上,所以 mutation 在被 commit 调用的时候只传入了一个参数 payload,但是 mutation 函数执行的时候就有了两个参数。
下面看一下 action:
按照刚才的分析 action 在被 dispatch 调用的时候会接收一个参数,但是 action 执行的时候会接收两个参数,第一个参数是个对象里面有六项,真的是包罗万象啊。。。我们看一下这个对象的六项
{
dispatch : local.dispatch,
commit:local.commit,
getter: local.getters,
state: local.state,
rootGetters:store.getters,
rootState:store.state
}
分为两种一种是 local 的、一种是 store 的。mutation 中好像也有使用 local,那么 local 的意义是什么呢?我们下一节会讲述 local 的含义以及 makeLocalContext、makeLocalGetters 两个函数的作用。还是要给个小线索,在模块树的层级很高的时候,我们在使用 state 的时候要一层一层找寻吗?
总结
dispatch 与 commit 在执行的时候,都是根据传入的全名 action、mutation 去 Store 实例的_actions、_mutations 中找到对应的方法,进行执行的。
dispatch 和 commit 中都使用了 this(指向 Store 实例),为了防止 this 的指向改变从而出现问题,就把原型上的 dispatch 与 commit 在 constructor 中处理到了实例本身,这个过程做了 this 指向的绑定(call)。
action 和 mutation 在执行的时候,参数都是在注册的时候就绑定了一部分,所以 action 与 mutation 在使用的时候可以访问到 state 等很多内容。
下一章;离开 action 与 mutation 来讨论一下 local 的含义以及 makeLocalContext、makeLocalGetters 两个函数的作用
我是一个应届生,最近和朋友们维护了一个公众号,内容是我们在从应届生过渡到开发这一路所踩过的坑,已经我们一步步学习的记录,如果感兴趣的朋友可以关注一下,一同加油~