写文章不容易,点个赞呗兄弟 专一 Vue 源码分享,文章分为白话版和 源码版,白话版助于理解工做原理,源码版助于了解内部详情,让咱们一块儿学习吧 研究基于 Vue版本 【2.5.17】vue
若是你以为排版难看,请点击 下面连接 或者 拉到 下面关注公众号也能够吧node
【Vue原理】Component - 源码版 之 建立组件VNode ios
今天就要开启咱们 Component 探索之旅,旅途有点长,各位请坐好,不要睡着了函数
内容的主题是,Component 的建立过程,从调用 component,到 component 挂载,到底经历了什么?欢迎来到 component 的心里世界学习
建议能够先看看白话版this
Component - 白话版 prototype
Component 建立,我主要分了两个流程3d
一、建立 组件 VNode 二、挂载 组件 DOM
每一个流程涉及源码都不少,因此每一个流程写一篇文章。没错了,今天讲的就是 建立组件 VNodecode
首先,咱们假定如今有这么一个模板,使用了 test 组件component
而后页面噼里啪啦执行到了 准备挂载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