Vue源码之 整体思路(未完工)

Vue.component一个组件和写在options的components属性中这个区别别的文章中讲,现在只讨论开始就是new Vue的情况

_init方法中,初始化一系列的东西,给vm赋值了各种属性,到最后判断

if (vm.$options.el) {
     vm.$mount(vm.$options.el);
  }

如果有el属性,那么开始把虚拟节点挂载,转化成真实节点。所以new Vue中的el属性是必不可少的。

$mount被重写,确定render方法(根据option的render函数 > 根据template属性 > 根据el节点挂载的那个真实节点的html),然后进入mountComponent方法,new一个Watcher,expOrFn方法(lazy为false,new方法中直接执行)中调用vm._update,进入vm.__patch__,根节点创建周期的preVnode用vm.$el代替,进入patch方法,if (isUndef(oldVnode)) ,这里由于oldVnode是vm.$el,所以不符合,往下走,vm.$el的值是一个普通节点,所以不会进入patchVnode方法,而是进入 createElm方法,创建完真实节点后,插入父节点的相应位置,删除之前的el绑定那个节点。

重点看createElm方法。如果虚拟节点是一个组件节点(有data.hook属性),会进入createComponent方法,分两种情况讨论。

1、如果option中没有render函数和template,只有el挂载的那个真实节点的信息,或者option的render函数返回和template属性的根节点也是一个普通节点,这个时候会跳过createComponent方法,进入isDef(tag)的判断,因为是真实节点,所以情况很简单:根据tag名称创建一个node,把data里的属性赋值就可以了。之后就是递归创建子节点。

2、如果render函数返回和template属性的根节点是一个组件节点,render函数的内部是这样的 with(this){return _c(tag,data,children)},其中tag是个组件节点的名字,那么刚才createElm方法中会进入createComponent,生成的虚拟节点中有一个ctor属性指向一个VueComponent方法(在闭包里的名称是Sub),Vnode.data.hook中有init方法,调用的是New VueComponent方法,VueComponent是继承了Vue的,也是一套打完,再调用$mount,(比newVue多这一个步骤是因为VueComponent对应的vm实例没有el属性,所以不会自动进入$mount)。在$mount中,进入_update方法,进入patch方法,又回去了,这里这次为了简化,假设组件里面只有一个普通节点,进入patch方法的时候,oldNode是vm的$el属性,注意这里会是undefined,所以会进入if (isUndef(oldVnode)) {createElm(vnode, insertedVnodeQueue);}创建新节点,刚才假设是一个普通节点,所以这次不会再走createComponent,直接创建普通节点,vNode.elm指向新创建的普通节点,插入,结束。考虑一种极端情况,组件节点的template的根节点还是一个组件节点这样的一个嵌套中,在创建周期中就会$mount----patch---createElm---createComponent---方法一直递归,直到遇到普通节点,而普通节点形成的真实节点会被嵌套的vNode.elm属性一直递归的传递出去。

更新周期:由于oldVNode和vnode都存在