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 相关文章推荐
取得一定长度的内容,处理中文
Dec 20 Javascript
jquery中ajax学习笔记3
Oct 16 Javascript
jquery checkbox 勾选的bug问题解决方案与分析
Nov 13 Javascript
纯HTML5制作围住神经猫游戏-附源码下载
Aug 23 Javascript
基于javascript实现样式清新图片轮播特效
Mar 30 Javascript
JQuery动态添加Select的Option元素实现方法
Aug 29 Javascript
jQuery轻松实现无缝轮播效果
Mar 22 jQuery
Node实战之不同环境下配置文件使用教程
Jan 02 Javascript
使用node.js实现微信小程序实时聊天功能
Aug 13 Javascript
基于javascript的无缝滚动动画1
Aug 07 Javascript
ES5和ES6中类的区别总结
Dec 21 Javascript
微信小程序组件生命周期的踩坑记录
Mar 03 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
WordPress中访客登陆实现邮件提醒的PHP脚本实例分享
2015/12/14 PHP
php打包压缩文件之ZipArchive方法用法分析
2016/04/30 PHP
php数据访问之查询关键字
2016/05/09 PHP
php下的原生ajax请求用法实例分析
2020/02/28 PHP
php 输出缓冲 Output Control用法实例详解
2020/03/03 PHP
IE7提供XMLHttpRequest对象为兼容
2007/03/08 Javascript
js实现的网页颜色代码表全集
2007/07/17 Javascript
JavaScript定义类的几种方式总结
2014/01/06 Javascript
JS获取及设置TextArea或input文本框选择文本位置的方法
2015/03/24 Javascript
js改变html的原有内容实现方法
2016/10/05 Javascript
详解微信小程序 页面跳转 传递参数
2016/12/08 Javascript
利用JS实现简单的日期选择插件
2017/01/23 Javascript
使用JS获取SessionStorage的值
2018/01/12 Javascript
使用Vue.js开发微信小程序开源框架mpvue解析
2018/03/20 Javascript
微信小程序自定义组件实现tabs选项卡功能
2018/07/14 Javascript
JavaScript进阶(二)词法作用域与作用域链实例分析
2020/05/09 Javascript
Node.js中出现未捕获异常的处理方法
2020/06/29 Javascript
Python中在for循环中嵌套使用if和else语句的技巧
2016/06/20 Python
python中pandas.DataFrame排除特定行方法示例
2017/03/12 Python
六行python代码的爱心曲线详解
2019/05/17 Python
flask框架单元测试原理与用法实例分析
2019/07/23 Python
Python datetime包函数简单介绍
2019/08/28 Python
pytorch中交叉熵损失(nn.CrossEntropyLoss())的计算过程详解
2020/01/02 Python
python selenium操作cookie的实现
2020/03/18 Python
怎么快速自学python
2020/06/22 Python
PyQt5 QDockWidget控件应用详解
2020/08/12 Python
HTML5的文档结构和新增标签完全解析
2017/04/21 HTML / CSS
什么是静态路由,其特点是什么?什么是动态路由,其特点是什么?
2013/07/26 面试题
Java的类与C++的类有什么不同
2014/01/18 面试题
机械电子工程毕业生自荐信
2013/11/23 职场文书
青蓝工程实施方案
2014/03/27 职场文书
老公给老婆的检讨书(精华篇)
2014/10/18 职场文书
丧事答谢词
2015/01/05 职场文书
餐饮服务员岗位职责
2015/02/09 职场文书
小学四年级作文之最感动的一件事
2019/11/01 职场文书
MySQL中rank() over、dense_rank() over、row_number() over用法介绍
2022/03/23 MySQL