前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【Vue原理】Event - 源码版 之 绑定标签DOM事件

【Vue原理】Event - 源码版 之 绑定标签DOM事件

作者头像
神仙朱
修改2019-08-03 13:55:05
5200
修改2019-08-03 13:55:05
举报

写文章不容易,点个赞呗兄弟 专注 Vue 源码分享,文章分为白话版和 源码版,白话版助于理解工作原理,源码版助于了解内部详情,让我们一起学习吧

研究基于 Vue版本 【2.5.17】

如果你觉得排版难看,请点击 下面链接 或者 拉到 下面关注公众号也可以吧

【Vue原理】Component - 源码版 之 创建组件VNode

今天就要开启我们 Component 探索之旅,旅途有点长,各位请坐好,不要睡着了

内容的主题是,Component 的创建过程,从调用 component,到 component 挂载,到底经历了什么?欢迎来到 component 的内心世界

建议可以先看看白话版

Component - 白话版

Component 创建,我主要分了两个流程

代码语言:txt
复制
1、创建 组件 VNode
2、挂载 组件 DOM

每个流程涉及源码都很多,所以每个流程写一篇文章。没错了,今天讲的就是 创建组件 VNode


场景设置

首先,我们假定现在有这么一个模板,使用了 test 组件

公众号
公众号
公众号
公众号

然后页面噼里啪啦执行到了 准备挂载DOM 的步骤(之前的部分跟本主题无关,跳过)

然后页面准备执行渲染函数 render,嗯,就是执行上面模板生成的渲染函数,如下

公众号
公众号

没有错,我们的 Vue 已经走到了这一步,那么我们的突破口是什么?

没错,就是 _c

代码语言:txt
复制
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

好的,我去前面探探路

代码语言:txt
复制
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

由于处理数据什么的,跟本内容无关,所以其他源码一律去掉,那么就只剩下两个流程

下面就开始这两个流程


创建组件构造函数

上面的源码中有两句话(如下),作用就是为组件创建一个构造函数!

代码语言:txt
复制
var baseCtor = context.$options._base;
Ctor = baseCtor.extend(Ctor);

看得懂吗?看懂了?好吧,那我就不讲了

算了,算了,还是讲吧,毕竟当时自己也是懵逼的

首先,context 是什么?

context 是执行整个渲染函数的上下文对象,很明显,这里就是页面的 实例vm 了

那么,$options 是什么?

$options 就是 实例自定义选项 和 全局选项合并之后的 产物

代码语言:txt
复制
Vue.prototype._init = function(options) {
    .....

    vm.$options = mergeOptions( // 把两个对象合并       

        vm.constructor.options, 
        options, vm
    );
    .....

}

vm.constructor 是什么?

没错,就是 Vue,你使用 new Vue 创建的页面,构造函数肯定是 Vue 啦

vm.constructor.options 是什么?

看下面

代码语言:txt
复制
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 中

代码语言:txt
复制
Vue.options._base = Vue;

那么,你应该能理解前面出现的源码了

代码语言:txt
复制
var baseCtor = context.$options._base;

没错!baseCtor 拿到的就是 Vue!!!!

然后还有一句

代码语言:txt
复制
Ctor = baseCtor.extend(Ctor);

既然 baseCtor 是 Vue,那 baseCtor.extend 是 Vue.extend?没有错!

正是他!完成了创建组件构造函数的伟大之举!!让我们一起来欣赏下

代码语言:txt
复制
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 - 源码版

跳到相关内容看就好了

代码语言:txt
复制
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

公众号
公众号
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-06-03,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 神仙朱 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 场景设置
  • 创建组件构造函数
    • 首先,context 是什么?
      • 那么,$options 是什么?
        • vm.constructor 是什么?
          • vm.constructor.options 是什么?
          • 创建组件外壳VNode
            • 那么这个外壳节点的作用是什么?
            • 总结
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档