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 相关文章推荐
js数字输入框(包括最大值最小值限制和四舍五入)
Nov 24 Javascript
jQuery常用且重要方法汇总
Jul 13 Javascript
JS弹出对话框实现方法(三种方式)
Dec 18 Javascript
基于jquery实现图片放大功能
May 07 Javascript
JavaScript实现打开链接页面的方式汇总
Jun 02 Javascript
利用jQuery插件imgAreaSelect实现图片上传裁剪(放大缩小)
Dec 02 Javascript
angularJS深拷贝详解
Mar 23 Javascript
jQuery实现上传图片前预览效果功能
Aug 03 jQuery
React操作真实DOM实现动态吸底部的示例
Oct 23 Javascript
JS中精巧的自动柯里化实现方法
Dec 12 Javascript
聊聊鉴权那些事(推荐)
Aug 22 Javascript
es6数组includes()用法实例分析
Apr 18 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(1)
2006/10/09 PHP
php小型企业库存管理系统的设计与实现代码
2011/05/16 PHP
PHP获取文件夹内文件数的方法
2015/03/12 PHP
基于linnux+phantomjs实现生成图片格式的网页快照
2015/04/15 PHP
PHP使用Pear发送邮件(Windows环境)
2016/01/05 PHP
简单的pgsql pdo php操作类实现代码
2016/08/25 PHP
使用PHP免费发送定时短信的实例
2016/10/24 PHP
PHP文件管理之实现网盘及压缩包的功能操作
2017/09/20 PHP
javascript forEach通用循环遍历方法
2010/10/11 Javascript
jquery中的mouseleave和mouseout的区别 模仿下拉框效果
2012/02/07 Javascript
jquery mobile changepage的三种传参方法介绍
2013/09/13 Javascript
JavaScript 变量、作用域及内存
2015/04/08 Javascript
JavaScript构造函数详解
2015/12/27 Javascript
AngularJs Understanding the Controller Component
2016/09/02 Javascript
学习使用bootstrap的modal和carousel
2016/12/09 Javascript
JS与jQuery实现子窗口获取父窗口元素值的方法
2017/04/17 jQuery
React Native使用fetch实现图片上传的示例代码
2018/03/07 Javascript
node.js连接mysql与基本用法示例
2019/01/05 Javascript
微信小程序按钮点击跳转页面详解
2019/05/06 Javascript
vue 父组件通过v-model接收子组件的值的代码
2019/10/27 Javascript
jQuery实现聊天对话框
2020/02/08 jQuery
vue webpack build资源相对路径的问题及解决方法
2020/06/04 Javascript
python通过自定义isnumber函数判断字符串是否为数字的方法
2015/04/23 Python
Python做文本按行去重的实现方法
2016/10/19 Python
Python实现base64编码的图片保存到本地功能示例
2018/06/22 Python
Python利用字典破解WIFI密码的方法
2019/02/27 Python
Python脚本去除文件的只读性操作
2020/03/05 Python
利用python对excel中一列的时间数据更改格式操作
2020/07/14 Python
paramiko使用tail实时获取服务器的日志输出详解
2020/12/06 Python
J2EE模式面试题
2016/10/11 面试题
物流专业大学的自我评价
2014/01/11 职场文书
好家长事迹材料
2014/01/23 职场文书
高三体育教学反思
2014/01/29 职场文书
服装创业计划书范文
2014/02/05 职场文书
大学生党员承诺书
2014/05/20 职场文书
品牌推广活动策划方案
2014/08/19 职场文书