三 · To be created or not to be created
有一道颇为经典的面试题是:data在Vue的哪个生命周期开始可以使用?如果有刷过面试题或者实际尝试过的前端可以脱口而出:created。然而真正的问题在于,为什么?

在Vue的文档里给出过一个很有用的生命周期示意图:

但这幅图没有也没必要把一些具体实现告诉你,本来框架的诞生就是为了让开发不用关心这些事情,但我们这里就是一探究竟,在new Vue()的初始化里,都发生了些什么事情。
上一节里我们提到的这个部分:
1 | initLifecycle(vm) |
在这里初始化了很多东西,第一个函数,望文生义,初始化生命周期,我们追踪到这个函数的内部:
1 | function initLifecycle (vm: Component) { |
initLifecycle函数接收一个Vue对象,并给这个对象添加许多属性。我们发现有_isMounted、_isDestroyed、_isBeingDestroyed这几个属性。在initEvents和initRender之后又有一个callhook函数,我们进去能看到:
1 | function callHook (vm: Component, hook: string) { |
这个函数接收两个参数,一个是vue实例,不用多说,一个是hook。hook其实就是Vue定义的生命周期,源码里传了个字符串“beforeCreate”进来。那这个有什么用呢?我们看这句:
1 | if (handlers) { |
handlers去找option里有没有这个属性,其实也就是我们在使用Vue实例时在特定生命周期写的那些函数。如果这个地方存在函数,就会执行invokeWithErrorHandling函数。这个函数大概长这样:
1 | function invokeWithErrorHandling ( |
我们先省略一些不是我们需要注意的部分,这个函数就是:
1 | function invokeWithErrorHandling ( |
这里面关键就在于通过apply和call去执行了我们传进来的handler,其实就是我们在option上定义的生命周期函数。apply和call很接近,一些具体的区别可以参考MDN文档。
这样我们就理解这个写法了:
1 | var app = new Vue({ |
我们在option上定义的生命周期函数就是这样被拿去执行的。我们这样一路看下来,会发现在callhook传入beforeCreate之前,根本没有和data或者method相关的内容,这个在哪里呢,我们来看后面的一个叫initState的函数:
1 | function initState (vm: Component) { |
看到initData了吗?这个函数也在同样的文件里,这里先不用看其中的具体内容了,因为现在我们已经足够可以回答这篇文章开头的题目:在callHook到beforeCreate的时候,也就是我们可以写函数的时候,data和method这些对象还没有被初始化出来,自然也就不可能在这个时候去调用它们啦。
(第三节 · 完)