这两节好难啊,大半天画了下深度遍历的整个流程图,再以视频中例子总结下整个组件patch的流程。
- 从vm._render函数开始,本质是执行createElement的过程,其中tag为App组件导出的对象,所以执行createComponent,传入tag
- createComponent过程对tag进行extend处理,返回组件构造器,给data注册hooks (后面用来组件实例化),创建组件vnode,传入data, 使用componentOptions来保存构造器,tag等等,返回App组件vnode
- 回到_render函数, 给App组件vode设置parent属性,此时是undefined,再执行vm._update方法
- vue实例绑定 _vnode 属性,指向App组件vnode(占位vnode),执行vm._patch, vnode为组件App vnode
- Vue实例patch, 此处其实是进入了createComponent,执行 组件 vnode 的 init hook
- init hook中, 组件vnode实例化,执行构造器的init方法,本质是vm._init, 只是vm指向组件实例
- 组件实例_init 过程, 执行initInternalComponent ,初始化组件实例的$options,parent指向 vue实例,_parentNode指向组件vnode
- 组件实例初始化生命周期,$parent 指向 vue实例,且 添加自身到 vue实例 children,完成父子关系绑定
- 组件实例化完成,实例保存在组件vnode的componentInstance,组件实例执行$mount 挂载
- 由于vue-loader编译完成已经有了render函数,执行 _render过程,创建组件实例vnode(渲染vnode)
- _render过程, vm.vnode=vm.vnode = vm.vnode=vm.options._parentNode ,将占位vnode保存在 组件实例vm的$vnode
- 其实组件实例的render函数中,tag为App组件的根节点div,这里直接创建普通节点vnode
- 执行组件实例 _update , vm._vnode = vnode 将渲染vnode保存在 _vnode属性,执行组件实例 _patch
- 普通节点,直接进入createElm 过程,创建真实dom保存到渲染vnode的elm,创建子节点,递归执行createElm
- 其中遇到helloworld组件又会执行createComponent,创建helloworld组件的实例,又回到了步骤5,由于helloworld中只有普通节点,patch的结果是在helloworld组件的渲染vnode中绑定了helloworld组件的真实dom(根节点和children),在init hook 完成时,会在app组件根节点下插入 helloworld 根节点dom
- app组件渲染vnode patch 完成,返回 vnode.elm
- app组件实例KaTeX parse error: Expected 'EOF', got ',' at position 16: el = vnode.elm,̲ app组件占位vnode i…el 挂载到 组件vnode的elm下
- 此时将 占位vnode的elm插入到 body 中, 此时视图渲染出来了,执行后面的逻辑,删除旧的div#app节点,Vue实例的patch过程完成,vm.$el = vnode.elm, 整体流程完成
其实就是深度递归的一个挂载过程: 从最外层Vue实例,再到App组件实例,再到helloworld组件实例,helloworld实例内部DOM节点创建完成后,helloworld组件就完成了,然后就是将helloworld 的根节点挂载到 App组件根节点,App组件根节点挂载完成后,就插入到body节点下,然后移除原来的div#app。的确需要注意老师一开始提到的占位vnode和渲染vnode。分别是用$vnode 和 _vnode保存的。 另外 activeInstance 表示当前正在init的实例,可能是vue实例,也可能是组件实例,会在创建组件实例时注册到后面的组件实例的 $parent, 形成实例间的父子关系