Vue如何实现组件的源码解析


Posted in Javascript onJune 08, 2017

官网上关于组件继承分为两大类,全局组件和局部组件。无论哪种方式,最核心的是创建组件,然后根据场景不同注册组件。

有一点要牢记,“Vue.js 组件其实都是被扩展的 Vue 实例”!

1. 全局组件

// 方式一
var MyComponent = Vue.extend({
  name: 'my-component',
  template: '<div>A custom component!</div>'
});
Vue.component('my-component', MyComponent);

// 方式二
Vue.component('my-component', {
  name: 'my-component',
  template: '<div>A custom component!</div>'
});

// 使用组件
<div id="example">
  <my-component></my-component>
</div>

主要涉及到两个静态方法:

  1. Vue.extend:通过扩展Vue实例的方法创建组件
  2. Vue.component:注册组件

先来看看Vue.extend源码,解释参考中文注释:

Vue.extend = function (extendOptions) {
 extendOptions = extendOptions || {};
 var Super = this;
 var isFirstExtend = Super.cid === 0;
 if (isFirstExtend && extendOptions._Ctor) {
  return extendOptions._Ctor;
 }
 var name = extendOptions.name || Super.options.name;
 // 如果有name属性,即组件名称,检测name拼写是否合法
 if ('development' !== 'production') {
  if (!/^[a-zA-Z][\w-]*$/.test(name)) {
   warn('Invalid component name: "' + name + '". Component names ' + 'can only contain alphanumeric characaters and the hyphen.');
   name = null;
  }
 }
 // 创建一个VueComponent 构造函数,函数名为‘VueComponent'或者name
 var Sub = createClass(name || 'VueComponent');
 // 构造函数原型继承Vue.prototype
 Sub.prototype = Object.create(Super.prototype);
 Sub.prototype.constructor = Sub;
 Sub.cid = cid++;
 // 合并Vue.options和extendOptions,作为新构造函数的静态属性options  
 Sub.options = mergeOptions(Super.options, extendOptions);
 //'super'静态属性指向Vue函数
 Sub['super'] = Super;
 // start-----------------拷贝Vue静态方法  
 // allow further extension
 Sub.extend = Super.extend;
 // create asset registers, so extended classes
 // can have their private assets too.
 config._assetTypes.forEach(function (type) {
  Sub[type] = Super[type];
 });
 // end-----------------拷贝Vue静态方法  
 // enable recursive self-lookup
 if (name) {
  Sub.options.components[name] = Sub;
 }
 // cache constructor:缓存该构造函数
 if (isFirstExtend) {
  extendOptions._Ctor = Sub;
 }
 return Sub;
};

可以看到,Vue.extend的关键点在于:创建一个构造函数function VueComponent(options) { this._init(options) },通过原型链继承Vue原型上的属性和方法,再讲Vue的静态函数赋值给该构造函数。

再看看Vue.component源码,解释参考中文注释:

// _assetTypes: ['component', 'directive', 'elementDirective', 'filter', 'transition', 'partial']
config._assetTypes.forEach(function (type) {
 // 静态方法Vue.component
 Vue[type] = function (id, definition) {
  if (!definition) {
   return this.options[type + 's'][id];
  } else {
   /* istanbul ignore if */
   if ('development' !== 'production') {
    if (type === 'component' && (commonTagRE.test(id) || reservedTagRE.test(id))) {
     warn('Do not use built-in or reserved HTML elements as component ' + 'id: ' + id);
    }
   }
   // 如果第二个参数是简单对象,则需要通过Vue.extend创建组件构造函数
   if (type === 'component' && isPlainObject(definition)) {
    if (!definition.name) {
     definition.name = id;
    }
    definition = Vue.extend(definition);
   }
   // 将组件函数加入Vue静态属性options.components中,也就是,全局注入该组件
   this.options[type + 's'][id] = definition;
   return definition;
  }
 };
});

方法Vue.component的关键点是,将组件函数注入到Vue静态属性中,这样可以根据组件名称找到对应的构造函数,从而创建组件实例。

2. 局部组件

var MyComponent = Vue.extend({
  template: '<div>A custom component!</div>'
});

new Vue({
  el: '#example',
  components: {
    'my-component': MyComponent,
    'other-component': {
      template: '<div>A custom component!</div>'
    }
  }
});

注册局部组件的特点就是在创建Vue实例的时候,定义components属性,该属性是一个简单对象,key值为组件名称,value可以是具体的组件函数,或者创建组件必须的options对象。

来看看Vue如何解析components属性,解释参考中文注释:

Vue.prototype._init = function (options) {
  options = options || {};
  ....
  // merge options.
  options = this.$options = mergeOptions(this.constructor.options, options, this);
  ...
};

function mergeOptions(parent, child, vm) {
  //解析components属性
  guardComponents(child);
  guardProps(child);
  ...
}

function guardComponents(options) {
  if (options.components) {
    // 将对象转为数组
    var components = options.components = guardArrayAssets(options.components);
    //ids数组包含组件名
    var ids = Object.keys(components);
    var def;
    if ('development' !== 'production') {
      var map = options._componentNameMap = {};
    }
    // 遍历组件数组
    for (var i = 0, l = ids.length; i < l; i++) {
      var key = ids[i];
      if (commonTagRE.test(key) || reservedTagRE.test(key)) {
        'development' !== 'production' && warn('Do not use built-in or reserved HTML elements as component ' + 'id: ' + key);
        continue;
      }
      // record a all lowercase <-> kebab-case mapping for
      // possible custom element case error warning
      if ('development' !== 'production') {
        map[key.replace(/-/g, '').toLowerCase()] = hyphenate(key);
      }
      def = components[key];
      // 如果是组件定义是简单对象-对象字面量,那么需要根据该对象创建组件函数
      if (isPlainObject(def)) {
        components[key] = Vue.extend(def);
      }
    }
  }
}

在创建Vue实例过程中,经过guardComponents()函数处理之后,能够保证该Vue实例中的components属性,都是由{组件名:组件函数}构成的,这样在后续使用时,可以直接利用实例内部的组件构建函数创建组件实例。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
斜45度寻路实现函数
Aug 20 Javascript
深入理解JavaScript作用域和作用域链
Oct 21 Javascript
原生js 秒表实现代码
Jul 24 Javascript
30分钟就入门的正则表达式基础教程
Feb 25 Javascript
kindeditor编辑器点中图片滚动条往上顶的bug
Jul 05 Javascript
纯javascript响应式树形菜单效果
Nov 10 Javascript
基于jQuery实现网页打印功能
Dec 01 Javascript
使用jquery获取url以及jquery获取url参数的实现方法
May 25 Javascript
js 数字、字符串、布尔值的转换方法(必看)
Apr 07 Javascript
基于js中的存储键值对以及注意事项介绍
Mar 30 Javascript
JS实现的哈夫曼编码示例【原始版与修改版】
Apr 22 Javascript
浅谈webpack 四个核心概念之Entry
Jun 12 Javascript
jquery Ajax实现Select动态添加数据
Jun 08 #jQuery
js canvas实现放大镜查看图片功能
Jun 08 #Javascript
vue实现一个移动端屏蔽滑动的遮罩层实例
Jun 08 #Javascript
微信小程序开发之map地图实现教程
Jun 08 #Javascript
详解AngularJS用Interceptors来统一处理HTTP请求和响应
Jun 08 #Javascript
微信小程序开发之实现自定义Toast弹框
Jun 08 #Javascript
微信小程序开发之toast等弹框提示使用教程
Jun 08 #Javascript
You might like
使用php转义输出HTML到JavaScript
2015/03/27 PHP
baidu博客的编辑友情链接的新的层窗口!经典~支持【FF】
2007/02/09 Javascript
js 数值项目的格式化函数代码
2010/05/14 Javascript
添加JavaScript重载函数的辅助方法2
2010/07/04 Javascript
JS随机漂浮广告代码具体实例
2013/11/19 Javascript
jquery创建表格(自动增加表格)代码分享
2013/12/25 Javascript
JS执行删除前的判断代码
2014/02/18 Javascript
新入门node.js必须要知道的概念(必看篇)
2016/08/10 Javascript
BootStrap table表格插件自适应固定表头(超好用)
2016/08/24 Javascript
js给table赋值的实例代码
2016/10/13 Javascript
Angularjs中的ui-bootstrap的使用教程
2017/02/19 Javascript
jQuery中过滤器的基本用法示例
2017/10/11 jQuery
详解Vue源码学习之callHook钩子函数
2018/07/25 Javascript
JS实现中英文混合文字溢出友好截取功能
2018/08/06 Javascript
[03:52]显微镜下的DOTA2第三期——英雄在无聊的时候干什么
2014/06/20 DOTA
[02:49]2018DOTA2亚洲邀请赛主赛事决赛日战况回顾 Mineski鏖战5局夺得辉耀
2018/04/10 DOTA
Python使用cx_Oracle模块将oracle中数据导出到csv文件的方法
2015/05/16 Python
Python实现获取域名所用服务器的真实IP
2015/10/25 Python
python函数中return后的语句一定不会执行吗?
2017/07/06 Python
Python之dict(或对象)与json之间的互相转化实例
2018/06/05 Python
基于随机梯度下降的矩阵分解推荐算法(python)
2018/08/31 Python
Python流程控制 if else实现解析
2019/09/02 Python
Selenium+BeautifulSoup+json获取Script标签内的json数据
2020/12/07 Python
HTML5 Canvas绘制圆点虚线实例
2015/01/01 HTML / CSS
html5的input的required使用中遇到的问题及解决方法
2018/04/24 HTML / CSS
小程序canvas中文字设置居中锚点
2019/04/16 HTML / CSS
分解成质因数(如435234=251*17*17*3*2,据说是华为笔试题)
2014/07/16 面试题
学校后勤人员职责
2013/12/27 职场文书
公务员培训心得体会
2013/12/28 职场文书
乡镇领导班子批评与自我批评材料
2014/09/23 职场文书
2015年初中元旦晚会活动总结
2014/11/28 职场文书
武夷山导游词
2015/02/03 职场文书
小学班级标语口号大全
2015/12/26 职场文书
Python3 类型标注支持操作
2021/06/02 Python
Redis集群节点通信过程/原理流程分析
2022/03/18 Redis
windows server 2016 域环境搭建的方法步骤(图文)
2022/06/25 Servers