写文章不容易,点个赞呗兄弟
专注 Vue 源码分享,文章分为白话版和 源码版,白话版助于理解工作原理,源码版助于了解内部详情,让我们一起学习吧
研究基于 Vue版本 【2.5.17】
如果你觉得排版难看,请点击 下面链接 或者 拉到 下面关注公众号也可以吧
【Vue原理】Component - 源码版 之 创建组件VNode
今天就要开启我们 Component 探索之旅,旅途有点长,各位请坐好,不要睡着了
内容的主题是,Component 的创建过程,从调用 component,到 component 挂载,到底经历了什么?欢迎来到 component 的内心世界
建议可以先看看白话版
Component 创建,我主要分了两个流程
1、创建 组件 VNode
2、挂载 组件 DOM
每个流程涉及源码都很多,所以每个流程写一篇文章。没错了,今天讲的就是 创建组件 VNode
场景设置
首先,我们假定现在有这么一个模板,使用了 test 组件
然后页面噼里啪啦执行到了 准备挂载DOM 的步骤(之前的部分跟本主题无关,跳过)
然后页面准备执行渲染函数 render,嗯,就是执行上面模板生成的渲染函数,如下
没有错,我们的 Vue 已经走到了这一步,那么我们的突破口是什么?
没错,就是 _c
function _createElement(
context, tag, data, children
) {
var vnode;
var options = context.$options
// 从父组件选项上拿到 对应的组件的选项
var Ctor = options.components[tag]
if (正常的HTML标签) { ....直接新建VNode }
else if ( Ctor ) {
vnode = createComponent(
Ctor, data, context,
children, tag
);
}
return vnode
}
今天讲的是 component,跳过其他,直接走到 第二个 if,嗯,他调用了一个 createComponent
好的,我去前面探探路
function createComponent(
Ctor, data, context,
children, tag
) {
var baseCtor = context.$options._base;
// 创建组件构造函数
Ctor = baseCtor.extend(Ctor);
var vnode = new VNode(
"vue-component-" + (Ctor.cid) + name,
data, undefined, undefined,
undefined, context,
{
Ctor: Ctor
}
);
return vnode
}
这个 createComponent 什么鬼的,作用大概是
1、创建组件构造函数
2、处理父组件给子组件的数据
3、创建组件 外壳 VNode
由于处理数据什么的,跟本内容无关,所以其他源码一律去掉,那么就只剩下两个流程
下面就开始这两个流程
创建组件构造函数
上面的源码中有两句话(如下),作用就是为组件创建一个构造函数!
var baseCtor = context.$options._base;
Ctor = baseCtor.extend(Ctor);
看得懂吗?看懂了?好吧,那我就不讲了
算了,算了,还是讲吧,毕竟当时自己也是懵逼的
首先,context 是什么?
context 是执行整个渲染函数的上下文对象,很明显,这里就是页面的 实例vm 了
那么,$options 是什么?
$options 就是 实例自定义选项 和 全局选项合并之后的 产物
Vue.prototype._init = function(options) {
.....
vm.$options = mergeOptions( // 把两个对象合并
vm.constructor.options,
options, vm
);
.....
}
vm.constructor 是什么?
没错,就是 Vue,你使用 new Vue 创建的页面,构造函数肯定是 Vue 啦
vm.constructor.options 是什么?
看下面
Vue.options = Object.create(null);
Vue.options.components = Object.create(null);
Vue.options.directives = Object.create(null);
Vue.options.filters = Object.create(null);
Vue.options._base = Vue;
Vue 在引入的时候,就完成了很多初始化的内容,这里就是其中给 Vue 增加options 的部分
你看到的 component 啊,filter 什么的啊,没错,保存的就是你全局注册的 component,filter
然后每个页面都能使用到 全局组件,全局filter 的原因
就是因为在 页面实例初始化的时候,把 页面选项 和 全局选项私下合并 了
然后,你应该能看到这一句,保存了 Vue 构造函数在 options._base 中
Vue.options._base = Vue;
那么,你应该能理解前面出现的源码了
var baseCtor = context.$options._base;
没错!baseCtor 拿到的就是 Vue!!!!
然后还有一句
Ctor = baseCtor.extend(Ctor);
既然 baseCtor 是 Vue,那 baseCtor.extend 是 Vue.extend?没有错!
正是他!完成了创建组件构造函数的伟大之举!!让我们一起来欣赏下
Vue.extend = function(extendOptions) {
// this 指向Vue
var Super = this;
var Sub = function VueComponent(options) {
this._init(options);
};
// 原型链继承
Sub.prototype = Object.create(Super.prototype);
Sub.prototype.constructor = Sub;
// Super 永远是 Vue,所以这里就是 合并全局选项
// 现在 Super 就是 vue,把 Vue 和 Sub 合并
// 是把一些全局的组件 指令合并到 Sub 中
Sub.options = mergeOptions(
// optios 还包括 mixins 注入的全局
Super.options,
extendOptions
);
return Sub
};
这个函数,会返回一个函数 VueComponent,他就是组件的构造函数!用来下篇文章创建组件实例的!!
上面的源码,做的事,简单说,就是继承父类Vue,然后合并 options 等
最后,提一下,所有实例的父类构造函数 Super 都是 Vue
并不是说,组件 a 有一个子组件b,然后组件b 的父类构造函数就是 a.contructor,这是不对的,永远是Vue,谁都是 Vue
创建组件外壳VNode
现在就是前面代码 createComponent 中的最后一步了
注意注意,这里创建的是【组件外壳节点】,内部节点还没有上场,在下篇文章才出现
至于,什么是外壳节点,去看下我的 VNode - 源码版
跳到相关内容看就好了
var vnode = new VNode(
"vue-component-" + Ctor.cid + name,
data, undefined, undefined,
undefined, context,
{
Ctor: Ctor
}
);
那么这个外壳节点的作用是什么?
1、保存刚创建好的组件构造函数,下篇文章中会调用到
2、保存父组件给子组件 关联的数据,比如 event,props 之类的(由于跟本主题无关,为了整洁,统统去掉了)
总结
Component 创建 外壳节点的流程,总结如下
1、页面渲染函数执行
2、_c('test') 执行
3、createElement 碰到 tag 是一个组件
4、从父组件中,拿到 test 组件的options,传入 createComponent (作用是创建构造函数和 VNode)
5、createComponent 调用 Vue.extend 创建组件构造函数
6、新建 VNode,并把构造函数和父组件给子组件的数据保存进去
7、返回 VNode
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。