【Vue2.x 源码学习】第四十三篇 - 组件部分 - 组件相关流程总结
一,前言
上篇,介绍了《组件部分-组件挂载流程简述》;
至此,【Vue2.x 源码学习】专栏核心的几个专题就初步完成了,包括:
响应式数据原理(第一篇 ~ 第十篇)
模板编译原理(第十一篇 ~ 第二十篇)
依赖收集、异步更新、生命周期(第二十一篇 ~ 第二十七篇)
diff 算法原理(第二十八篇 ~ 第三十三篇)
组件部分(第三十四篇 ~ 第四十二篇)
简单总结一下问题,及专栏后续的完善工作:
响应式数据原理部分:写的有些乱,顺序上还需要再做调整,好在事情讲清楚了;
模板编译原理部分:部分内容仍需要再细化,要把流程讲清楚,让人一看就懂;
diff 算法部分:还需要添加一些必要图示和动画,这部分不难,但做好不容易;
组件部分:近半月工作比较忙,这部分质量并不高,二轮首先优化这块;
目标:修复问题较大的几篇文章,并在过程中梳理规划出第三轮优化的内容;
本篇对组件部分的几篇文章做一次阶段性总结《组件部分-组件相关流程总结》;
二,主要流程划分
Vue.component 的实现
Vue.extend 的实现
组件合并的实现
组件编译的实现
创建组件的虚拟节点
组件生命周期的实现
创建组件的真实节点
组件挂载的实现
三,各流程的实现简述
Vue.component 的实现
在 Vue 初始化流程中,会对全局 API 做集中处理,创建出 Vue.component API;
将 Vue 保存到全局对象
Vue.options
上,以便后续流程中,组件可通过vm.$options._base
获取到 Vue;备注:由于子类组件上没有 extend 方法,需通过 Vue 才能将组件定义对象处理为组件的构造函数;在 Vue.component 中,若组件定义为对象,使用 Vue.extend 处理为组件构造函数;
扩展
Vue.options
对象,将全局组件定义维护到Vue.options.components
上;
Vue.options.components
的作用:
利用全局对象
vm.constructor.options
完成全局组件与局部组件的合并;通过组件虚拟节点的标签名,查询对应组件的构造函数,完成组件的实例化;
2,Vue.extend 的实现
Vue.extend
:使用基础 Vue 构造器,创建一个子类;Vue.extend
内部会根据组件定义生成一个继承于 Vue 原型的组件子类 Sub;修复 constructor 指向问题:由于
Object.create
会产生一个新的实例作为子类的原型,导致 constructor 指向错误,应指向当前子类 Sub;返回组件的构造函数 Sub,
Vue.component
中将对组件构造函数进行全局映射
3,组件合并的实现
此时,
vm.constructor.options
包含了Vue.options.components
中的全局组件;执行
new Vue
时,会进行组件的初始化,进入 _init 方法;在
_init
方法中,通过mergeOptions
方法:将 new Vue 传入的局部组件定义options
与全局组件定义进行合并操作;在
mergeOptions
方法中,通过策略模式,获取到预设的组件合并策略函数;组件的合并策略:创建新对象继承于全局组件定义,并将局部组件定义添加到新对象中;此时会优先在新对象中查找局部组件定义,若未找到,会继续通过链上的继承关系查找全局组件定义;
需要注意:
在
vm.constructor.options
中的全局组件,可能已被 Vue.extend 处理为函数(组件的构造函数);在
options
中的局部组件,不会被 Vue.extend 处理,此时还是一个对象;
4,组件编译的实现
模板编译流程相似:组件模板 -> AST 语法树 -> render 函数
5,创建组件的虚拟节点
render 函数中,通过 createElement 方法:生成组件的虚拟节点;
在
createElement
方法中,进行标签筛查,若未非普通标签则视为组件;获取组件定义(有可能是构造函数),并通过 createComponent 方法,创建组件虚拟节点 componentVnode;在
createComponent
中,当获取到的组件定义 Ctor 为对象时,需先通过Vue.extend
处理为组件的构造函数;获取事先保存在全局
vm.$options._base
中的 Vue,实现 Vue.extend 生成组件构造函数;通过 vnode 方法生成组件的虚拟节点 componentVnode,将组件相关信息封装到 componentOptions 对象中;完整的 componentOptions 包括:Ctor、propsData、listeners、tag、children;
注意,所有组件最终都会被 Vue.extend 处理成为组件的构造函数:
全局组件:在 Vue.component 内部可能已经被 Vue.extend 处理完成;
局部组件:在 createComponent 创建组件虚拟节点时,被 Vue.extend 处理;
6,组件生命周期的实现
createComponent 方法创建组件虚拟节点过程中,通过扩展 data 属性,为组件添加生命周期钩子函数;
组件初始化时,通过执行 init 钩子函数,实现组件的实例化并完成页面挂载;
注意,将组件实例保存到虚拟节点上 vnode.componentInstance,便于后续获取组件真实节点,完成组件的挂载操作;
7,创建组件的真实节点
createElm 方法中,通过执行 createComponent 方法,将组件虚拟节点生成真实节点并返回;
备注:createComponent 执行完毕后,vnode.componentInstance 赋值为组件实例,vnode.componentInstance.$el 即为组件的真实节点
在 createComponent 方法中,若存在 hook 即为组件,通过组件 init 钩子函数,进行组件初始化操作;
组件的 init 钩子函数中,通过 new Ctor 实例化组件时,会执行 _init 进行组件的初始化,此时,vm.$options.el 为空,不会自动挂载组件;
通过
child.$mount()
进行组件挂载操作,由于 $mount 参数 el 为 null,所以也不会进行挂载;
生成组件 render 函数后,执行 mountComponent 进行组件的挂载;
updateComponent 中通过 _render 产生组件虚拟节点:
vm.render 执行完成后,继续执行 _update 方法
初渲染 preVnode 为空,patch 方法中 oldVnode 为 null(组件的 el 为空),使用组件的虚拟节点,创建出组件的真实节点并返回:
返回的 vm.$el 即为组件的真实节点;
8,组件挂载的实现
createElm 方法内中,会递归的生成真实节点,并插入对应的父节点中;
createElm 为深度优先遍历,最终将完整的 div 挂载到页面上;
备注:
_update 执行完成,继续回到 mountComponent 方法,执行 beforeCreate 钩子、生成组件独立的渲染 watcher、执行 mounted 钩子,完成组件挂载
四,结尾
本篇,对组件相关流程与实现进行了简单总结;
【Vue2.x 源码学习】第一阶段完结,后续将持续进行优化;
评论