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 charAt的使用示例
Feb 18 Javascript
jquery五角星评分插件示例分享
Feb 21 Javascript
瀑布流布局代码一例
Apr 11 Javascript
实例说明为什么不要行内使用javascript
Apr 18 Javascript
跟我学Node.js(四)---Node.js的模块载入方式与机制
Jun 04 Javascript
js实现点击添加一个input节点
Dec 05 Javascript
jquery实现模拟百分比进度条渐变效果代码
Oct 29 Javascript
基于jQuery的ajax方法封装
Jul 14 Javascript
vue项目中公用footer组件底部位置的适配问题
May 10 Javascript
vue element table 表格请求后台排序的方法
Sep 28 Javascript
JavaScript实现横版菜单栏
Mar 17 Javascript
JavaScript setTimeout()基本用法有哪些
Nov 04 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 substr 截取字符串出现乱码问题解决方法[utf8与gb2312]
2011/12/16 PHP
通过修改Laravel Auth使用salt和password进行认证用户详解
2017/08/17 PHP
php封装的pdo数据库操作工具类与用法示例
2019/05/08 PHP
php session_decode函数用法讲解
2019/05/26 PHP
javascript removeChild 使用注意事项
2009/04/11 Javascript
Javascript 读书笔记索引贴
2010/01/11 Javascript
ExtJS 工具栏 分页事件参数
2010/03/05 Javascript
javascript的propertyIsEnumerable()方法使用介绍
2014/04/09 Javascript
JavaScript中对循环语句的优化技巧深入探讨
2014/06/06 Javascript
js实现图片无缝滚动
2015/12/23 Javascript
分享jQuery插件的学习笔记
2016/01/14 Javascript
微信小程序 wx.uploadFile无法上传解决办法
2016/12/14 Javascript
jQuery实现选项卡功能(两种方法)
2017/03/08 Javascript
微信小程序下拉刷新界面的实现
2017/09/28 Javascript
javascript 函数的暂停和恢复实例详解
2020/04/25 Javascript
Ajax获取node服务器数据的完整步骤
2020/09/20 Javascript
JavaScript实现网页留言板功能
2020/11/23 Javascript
Tornado Web服务器多进程启动的2个方法
2014/08/04 Python
Python实现国外赌场热门游戏Craps(双骰子)
2015/03/31 Python
详解Python的Twisted框架中reactor事件管理器的用法
2016/05/25 Python
200行自定义python异步非阻塞Web框架
2017/03/15 Python
python实现彩票系统
2020/06/28 Python
python自动化UI工具发送QQ消息的实例
2019/08/27 Python
python DataFrame转dict字典过程详解
2019/12/26 Python
pygame用blit()实现动画效果的示例代码
2020/05/28 Python
Pytorch 解决自定义子Module .cuda() tensor失败的问题
2020/06/23 Python
HTML5 Canvas标签使用收录
2009/07/07 HTML / CSS
Canvas绘制浮动球效果的示例
2017/12/29 HTML / CSS
瑞典耳机品牌:URBANISTA
2019/12/03 全球购物
个人自我鉴定总结
2014/03/25 职场文书
幼儿园见习报告范文
2014/10/30 职场文书
2014年秘书工作总结
2014/11/25 职场文书
python requests模块的使用示例
2021/04/07 Python
React Hook用法示例详解(6个常见hook)
2021/04/28 Javascript
python办公自动化之excel的操作
2021/05/23 Python
nginx之内存池的实现
2022/06/28 Servers