写文章不容易,点个赞呗兄弟 专一 Vue 源码分享,文章分为白话版和 源码版,白话版助于理解工做原理,源码版助于了解内部详情,让咱们一块儿学习吧 研究基于 Vue版本 【2.5.17】vue
若是你以为排版难看,请点击 下面连接 或者 拉到 下面关注公众号也能够吧node
【Vue原理】Component - 源码版 之 建立组件VNode ios
今天就要开启咱们 Component 探索之旅,旅途有点长,各位请坐好,不要睡着了bash
内容的主题是,Component 的建立过程,从调用 component,到 component 挂载,到底经历了什么?欢迎来到 component 的心里世界函数
建议能够先看看白话版学习
Component 建立,我主要分了两个流程this
一、建立 组件 VNode
二、挂载 组件 DOM
复制代码
每一个流程涉及源码都不少,因此每一个流程写一篇文章。没错了,今天讲的就是 建立组件 VNodespa
首先,咱们假定如今有这么一个模板,使用了 test 组件prototype
而后页面噼里啪啦执行到了 准备挂载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 什么鬼的,做用大概是
一、建立组件构造函数
二、处理父组件给子组件的数据
三、建立组件 外壳 VNode
因为处理数据什么的,跟本内容无关,因此其余源码一概去掉,那么就只剩下两个流程
下面就开始这两个流程
上面的源码中有两句话(以下),做用就是为组件建立一个构造函数!
var baseCtor = context.$options._base;
Ctor = baseCtor.extend(Ctor);
复制代码
看得懂吗?看懂了?好吧,那我就不讲了
算了,算了,仍是讲吧,毕竟当时本身也是懵逼的
context 是执行整个渲染函数的上下文对象,很明显,这里就是页面的 实例vm 了
$options 就是 实例自定义选项 和 全局选项合并以后的 产物
Vue.prototype._init = function(options) {
.....
vm.$options = mergeOptions( // 把两个对象合并
vm.constructor.options,
options, vm
);
.....
}
复制代码
没错,就是 Vue,你使用 new Vue 建立的页面,构造函数确定是 Vue 啦
看下面
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
如今就是前面代码 createComponent 中的最后一步了
注意注意,这里建立的是【组件外壳节点】,内部节点尚未上场,在下篇文章才出现
至于,什么是外壳节点,去看下个人 VNode - 源码版
跳到相关内容看就行了
var vnode = new VNode(
"vue-component-" + Ctor.cid + name,
data, undefined, undefined,
undefined, context,
{
Ctor: Ctor
}
);
复制代码
一、保存刚建立好的组件构造函数,下篇文章中会调用到
二、保存父组件给子组件 关联的数据,好比 event,props 之类的(因为跟本主题无关,为了整洁,通通去掉了)
Component 建立 外壳节点的流程,总结以下
一、页面渲染函数执行
二、_c('test') 执行
三、createElement 碰到 tag 是一个组件
四、从父组件中,拿到 test 组件的options,传入 createComponent (做用是建立构造函数和 VNode)
五、createComponent 调用 Vue.extend 建立组件构造函数
六、新建 VNode,并把构造函数和父组件给子组件的数据保存进去
七、返回 VNode