单文件组件生成 VNode 过程
本节主要简述单文件组件生成组件 VNode 的过程,下节将简述组件 vnode 生成真实 DOM 节点的过程,部分代码就暂时不上图了!
对于单文件组件 *.vue 文件,是通过 import 导入,经过 vue-loader 编译生成组件的信息对象,该对象包含 render() 函数,以及处理生命周期 hook 为 Array 类型,方便后续声明周期的合并。通过 import 导入的 app.vue 组件,组件信息对象 App 如下图所示:
初始化的历程与 new Vue()初始化 new Vue() & JSX(一) 初始化基本一致,所以就不过多介绍,下面只从不同的地方开始介绍。
首先 render() 函数生成组件 vnode 的过程有较大的的区别,接下来重点介绍一下。
因为是开发者手写 render() 函数,所以执行 vm._$createElemen(a,b,c,d) 函数,此时的参数 a 就是组件信息对象,再调用函数 createElement(vm,a,b,c,d,true) 进行参数处理后 d=2 然后调用 _createElement(vm,a,b,c,d) 函数。
在 _createElement(vm,tag) 函数内首先对参数进行校验(此处与上章节所走流程基本一致,所以省略),又对 tag 进行分类处理。此时的 tag 为编译后的组件信息 App,所以执行 vnode = createComponent(App) 然后返回组件 vnode .
在 createComponent(ctor:组件信息) 函数内主要有三个关键流程:构造子类(子组件)构造函数、安装组件的钩子函数、生成组件 vnode,具体如下:
1、构造子类(子组件)构造函数
- 首先缓存
vm.$options._base属性,即为Vue类构造函数。然后判断ctor若是对象类型,则通过ctor = Vue.extend(ctor)函数通过原型继承将一个对象转为一个继承于Vue类的构造函数Sub并返回该子类(VueComponent). - 在
Vue.extend(ctor)内,先定义子类Sub = function VueComponent(){},然后通过Sub.prototype = Object.create(Vue.prototype); Sub.prototype.constructor = Sub;原型继承方式生成子类的原型对象,然后又对Sub本身扩展静态属性。 - 首先合并属性
Sub.options = mergeOptions(Vue.options, ctor),然后Sub['super'] = Super指向父类的属性。然后又配置子类静态属性Sub.cid,接着对配置中的属性Sub.options.props/computed进行了初始化。 - 设置全局静态方法
Sub.extend/mixin/use/component/filter/directive()通过Vue类进行赋值。以上扩展属性的目的就是让Sub拥有与Vue一样的能力。 - 接着缓存父类的属性:
Sub.superOptions = Super.options; - 缓存组件信息:
Sub.extendOptions = ctor; - 缓存子构造函数的
options:Sub.sealedOptions = Sub.options; - 最后又缓存了子类构造函数
Sub()在组件信息属性ctor._Ctor上,这样避免多次对同一组件重复构造,返回return Sub.
2、安装组件钩子函数
- 通过
installComponentHooks(data)函数安装组件的钩子函数。整个过程就是遍历componentVNodeHooks对象的钩子函数合并到组件的data.hook属性上。 - 钩子函数包括:
init()、prepatch()、insert()、destroy() - 合并策略:若
data.hook[i]不存在,则直接赋值componentVNodeHooks[i]函数。若data.hook[i]存在,则data.hook[i] = func && func._merged=true被赋值为函数func,而func函数内依次componentVNodeHooks[i]、data.hook[i]执行两个组件钩子函数。
3、生成组件 vnode
- 先设置组件名
tag = vue-component-{cid}-{name},然后再通过构造函数实例化组件new VNode(tag, data, undefined..., context, componentOptions)生成组件vnode对象,包含属性componentOptions:{Ctor...}其中Ctor为子组件构造器,组件的vnode是没有children属性的。
最后返回组件 vnode 对象并退出 createComponent() 函数。