这一篇博客,我们来看下 Vue 的生命周期。
首先,我们先写一个简单的 new Vue(),就只有一个简单的钩子函数。
很明显,它肯定会打印出来。那么我们一共都有哪些钩子函数呢?
我们可以直接到 Vue 的源码中看到。
我们可以看到,Vue 中的生命周期钩子函数,都是存在 LIFECYCLE_HOOKS 中的。
那么我们可以直接复制过来。
我们可以看到,有一个新加入的钩子函数 errorCaptured。
这个在我前面 Vue 基础的博客里面有提到过,就是当一个子孙组件报错的时候,它就会被调用。
接下来,我们就来编写钩子函数的自定义策略。
首先,我们需要给 LIFECYCLE_HOOKS 这个数组来进行遍历。
然后就会给 strats 来拓展对应的 hook 策略,对应的都是一个 mergeHook 的函数。
然后我们给 mergeHook 进行传参,mergeHook(parentVal, childVal)。
那么 parentVal, childVal 分别是什么呢?
首先,我们回到 mergeOptions 方法里面,看下 parent 和 child 分别是谁。
parent 就是 Vue.options,那它里面有没有 created 这个选项呢,很明显,没有。
child 就是我们 new Vue() 传进来的对象,我们传的对象里面才有 created 这个属性。
所以我们就知道了,created 在 child 里面是存在的。
那么,然后调用 mergeField 之后,我们就会用 strats[key] 来检测有没有当前这个配置。
然后就发会发现 created 是有配置的,那我们就直接找到了 mergeHook 这个方法。
然后我们就会给它传参:parent[key], child[key], vm, key。
parent[key] 指的就是 Vue.options.created,很明显,没有,那么就是 undefined。
child[key] 指的就是我们所传对象里面的 created 对应的函数。
所以我们就明确了,mergeHook(parentVal, childVal) 中的 parentVal 是 undefined,childVal 就是一个函数。
那么然后,我们就是怎么来做数据的处理了。
首先,我们会来判断一下 childVal 是否有值。
如果说 childVal 有值,那我们在来判断一下 parentVal 是否有值。
那如果说 parentVal 是有值的情况下,那我们就会直接来调用数组的 concat 方法,来进行数组的合并。
合并谁?合并 childVal。
那如果说 parentVal 没有值的情况呢?
那我们就会来调用 Array.isArray 这个方法,来检测一下 childVal 是不是一个数组。
如果说 childVal 是一个数组,那我们就直接返回 childVal。
如果说是个函数,不是一个数组,那我们就直接包装成一个数组。
那如果说 childVal 没有值呢?
那我们就直接返回 parentVal。
根据这个表达式,它会告诉我们 childVal 如果是一个数组,我们会直接返回出去。
那我们是不是可以这么做,我们在 new Vue() 中传入的对象中, created 能不能对应的也是一个数组呢,并且这个数组里面对应的,也是一系列的函数。
因为我们看过源码就知道,childVal 会做一个检测,如果是数组,就返回自身。
如果不是数组,那就包装成一个数组。
那是不是也意味着我们在写 Vue 的时候,created 我们是不是也可以写一个数组呢?
通过打印结果,我们发现,原来还可以这样子来写。原来,Vue 最终一定会把这些钩子组装成一个数组。
那么为什么要组装成一个数组呢?
原因是因为,如果说接下来我们在这些子组件,或子孙组件里面,我们也需要把子组件里面的 created 对应的一些钩子,跟我们的父组件里面的 created 对应的一些钩子,来做一些合并,合并成一个数组。
然后我们也可以来看一下,我们可以直接打印 vm.$options,来看下 created 的类型。
很明显,Vue 源码中,打印的 created 就是个数组。
那如果说我们不写数组,直接对应的是一个函数呢?
我们可以看到,即使我们对应的是一个函数,Vue 最终也会把它包装成一个数组。
然后我们回到代码继续,当我们判断 childVal 有值的时候,还会去检测 parentVal 是否有值。
如果说 parentVal 有值,我们就会 parentVal.concat(childVal),也就是把这2个数组来进行合并。
那么这种情况是怎么发生的呢?
我们引用自己写的 Vue,举个例子:
我们可以看到,这时候 created 里面就是2个函数了。