重读vue源码(2)—— new Vue()发生了什么,为何狗焕闷闷不乐?

169 阅读5分钟

今天,兄弟狗焕闷闷不乐的找到我。

面试官问他:new Vue()发生了什么,能给我说说吗?

狗焕拽进了拳头,憋了半天,敢怒不敢言。

我看穿了他的心事,写下这篇文档,希望我的兄弟狗焕可以如从前那样,快乐的在夕阳下奔跑,开心起来。


为了更好的让狗焕理解整个过程,上一篇我们讲的重读vue源码(1)—— 调试环境搭建,为何狗焕心事重重可以派上用场了,我们通过打断点的方式一步一步的向前看,代码怎么走下去的。

现在进入正题,发车了。

以下是本文目录:

- 从打断点开始观察实例化经历了哪些过程
    - 第一步:进入`src/core/instance/index.ts`文件
    - 第二步:进入`src/core/instance/init.ts`文件
    - 第三步:进入`src/core/instance/lifecycle.ts`,查看`initLifecycle`执行代码
    - 第四步:进入`src/core/instance/events.ts`,查看`initEvents`执行代码
    - 第五步:进入`src/core/instance/render.ts`,查看`initRender`执行代码
    - 第六步:进入`src/core/instance/lifecycle.ts`,查看`callHook`执行代码
    - 第七步:进入`src/core/instance/inject.ts`,查看`initInjections`执行代码
    - 第八步:进入`src/core/instance/state.ts`,查看`initState`执行代码
    - 第九步:进入`src/core/instance/inject.ts`,查看`initProvide`执行代码
    - 对断点结果总结一下
- 手写new Vue(),不再让你手足无措
- 手写代码地址
- 写到后面

从打断点开始观察实例化经历了哪些过程

从上一篇重读vue源码(1)—— 调试环境搭建,为何狗焕心事重重的01test.html这个文件作为例子讲解。

在实例化的这里打断点,往下看它会执行哪些步骤。 image.png

第一步:进入src/core/instance/index.ts文件

实例化进入到上述文件中,执行了_init的原型方法。

“好,继续看这个this._init()方法执行了什么鬼。”狗焕激动的跟我说着。 image.png

第二步:进入src/core/instance/init.ts文件

image.png

前面主要是一些变量的定义、边界条件的判断。最核心的是下面这部分的代码。

vm._self = vm
initLifecycle(vm)
initEvents(vm)
initRender(vm)
callHook(vm, 'beforeCreate', undefined, false /* setContext */)
initInjections(vm) // resolve injections before data/props
initState(vm)
initProvide(vm) // resolve provide after data/props
callHook(vm, 'created')

狗焕说:“我大概知道执行一些什么事情了”。让我们进一步看这部分核心代码执行了一些什么方法吧。

好的,那我把上面的这几个参数一步步的打断点向下执行吧。

第三步:进入src/core/instance/lifecycle.ts,查看initLifecycle执行代码

image.png

vm.$parent、vm.$root、vm._watcher等变量进行赋值和初始化的操作。

第四步:进入src/core/instance/events.ts,查看initEvents执行代码

image.png

初始化vm._events,然后通过updateComponentListeners将vm和配置中传入的_parentListeners作为入参,去执行一个什么监听,大概知道这样一个逻辑,没有看的太深。

第五步:进入src/core/instance/render.ts,查看initRender执行代码

image.png

将createElement挂载到vm上,并且对vm.$attrsvm.$listeners设置成响应式的数据。

第六步:进入src/core/instance/lifecycle.ts,查看callHook执行代码

image.png

执行生命周期钩子函数,循环遍历传进来的生命周期函数(所以这里存储生命周期函数应该是一个数组)。

第七步:进入src/core/instance/inject.ts,查看initInjections执行代码

image.png

对inject接收到的数据设置成响应式。

第八步:进入src/core/instance/state.ts,查看initState执行代码

image.png

首先兼容了Vue@3的Composition API,集成了initSetup方法。然后对props、methods、data、computed、watch等进行初始化。

第九步:进入src/core/instance/inject.ts,查看initProvide执行代码

image.png

对传入的provide参数到的数据设置成响应式。

对断点结果总结一下

// 对`vm.$parent、vm.$root、vm._watcher`等变量进行赋值和初始化的操作
initLifecycle(vm)
// 初始化vm._events,通过updateComponentListeners将vm和_parentListeners执行监听方法
initEvents(vm)
// 将createElement挂载到vm上,并且对`vm.$attrs`和`vm.$listeners`设置成响应式的数据
initRender(vm)
// 执行beforeCreate生命周期钩子函数
callHook(vm, 'beforeCreate', undefined, false)
// 对inject接收到的数据设置成响应式
initInjections(vm)
// 对props、methods、data、computed、watch等数据进行初始化。
initState(vm)
// 对传入的provide参数到的数据设置成响应式
initProvide(vm)
// 执行created生命周期钩子函数
callHook(vm, 'created')

手写new Vue(),不再让你手足无措

方便狗焕记得更深刻,我把上面这部分的学习写成一段手写代码,让他学习更轻松。

html模块: image.png

js模块: image.png

执行一下,看看会发生什么。

image.png

跟预期一样,很棒棒。

手写代码地址

为了帮助友友们更好的理解和学习vue源码知识,我把手写代码放在点击这里。打开链接,就可以看到我手写的这段代码了。后续的一些源码重读部分的手写代码,也会放在一起。

防止一会找不到丢失了,友友们可以star或者fork这个手写项目,以后打开自己的码云主页就能看到啦,学习更高效更方便。

写在最后

看完这篇文档,果然兄弟狗焕开心的回到电脑前开始学习,原来学习真的可以很轻松。

如果这篇文档对你有帮助,欢迎点赞、关注或者在评论区留言,我会第一时间对你的认可进行回应。精彩内容在后面,防止跑丢,友友们可以先关注我,每一篇文章都能及时通知不会遗失。