Vue源码主线路整理

第二次来梳理Vue源码逻辑了。第一次因为不熟悉,梳理的很细致才弄懂。第二次就更有大局观一些了,这次我主要抓住流程的重点顺利走完流程就好了。主路线是按执行顺序进行排列的。

入口是哪里开始的?

init()。合并配置,初始化事件中心、初始化生命周期、初始化数据响应化、初始化渲染等等。

render函数是什么时候生成的?

vm.$mount。把el或者 template字符串或.vue文件dom文档,解析成render函数(我们使用webpack的话,直接解析成render函数,此步骤我们在webapck中完成)。

render函数-》Vnode-》真实DOM 的步骤?

mountComponent(主要代码:updateComponent = () => { vm._update(vm._render(), hydrating)} 和 vm._watcher = new Watcher(vm, updateComponent, noop))。其核心就是先调用 vm._render 方法,生成虚拟 Node。再实例化一个渲染Watcher,在它的回调函数中会调用 updateComponent 方法,最终调用 vm._update更新 DOM。

render函数是如何变成Vnode的?

vm._render,把render函数渲染成vnode。模板编译成的 render 函数使用vm._c 方法渲染,用户手写 render函数用vm.$createElement方法渲染。 这俩个方法都在入口的初始化渲染中进行了定义。它们传入的参数相同,并且内部都调用了 createElement 方法。createElement,创建vnode并返回。该方法有5个参数,也就是创建vnode的原材料,context (VNode 的上下文环境);tag (标签,是一个字符串或者一个 Component);data (VNode的数据);children( VNode 的子节点,是任意类型的);normalizationType(子节点规范的类型)。

注意
1、编译生成的render函数的children数组中内容(包括普通节点和组件节点)已经是vnode类型了,在编译阶段已经完成了,只是对某些情况需要规范数据格式。所以下面的render函数转化成vnode数据格式,是对根节点做的操作,而对children只是做了规范化操作。
2、createElement方法执行了多少次?tag标签又是什么?在编译成render函数时,children已经是vnode类型怎么理解,学到编译阶段就明白了。

Vue源码主线路整理_第1张图片
在编译模板的时候,会解析到各个节点的 tag,在生成 render 函数的时候,作为参数传入,并最终传给 createElement 函数。这个 _c 最终都会执行 createElement,你可以看到,对于一个组件嵌套多层节点的情况,会生成多个 createElement 函数,整个 render 函数返回的 vnode,其实是一个 vnode tree。

createElement主要做了那些事情?

1、规范vnode的子节点children。render 函数如果是编译生成的,理论上编译生成的 children 都已经是 VNode 类型了(除了functional component 函数式组件返回的是一个数组而不是一个根节点)。children是一个数组,组员是一个个vnode,它们也有自己的children数组,这样就形成了一个 VNode Tree,它很好的描述了我们的 DOM Tree。其中对children的处理有2个方法simpleNormalizeChildren(针对render函数是编译生成且函数式组件返回的是一个数组而不是一个根节点)和normalizeArrayChildren(针对v-solt / v-for编译生成有嵌套数据的情况或者用户手写render函数),打平children数组。
2、根据tag不同类型,以不同方式创建vnode。1、若tag是string 类型,若是内置节点,就直接创建vnode,若是已注册的组件名,就createComponent,创建组件类型vnode,否则创建未知标签vnode。2、若tag是Component类型(比如),createComponent,创建组件类型vnode节点。   

注意:这里对不同tag类型用不同方式生成vnode节点,是对main.js中new Vue({options})中options中的模版传入方式做判断。1、直接写