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 相关文章推荐
jQueryPad 实用的jQuery测试工具(支持IE,chrome,FF)
May 22 Javascript
一个收集图片的bookmarlet(js 刷新页面中的图片)
May 27 Javascript
jquery删除数据记录时的弹出提示效果
May 06 Javascript
JavaScript实现数组在指定位置插入若干元素的方法
Apr 06 Javascript
基于JavaScript实现自动更新倒计时效果
Dec 19 Javascript
在JS中如何把毫秒转换成规定的日期时间格式实例
May 11 Javascript
Angular4学习笔记之准备和环境搭建项目
Aug 01 Javascript
angular.js和vue.js中实现函数去抖示例(debounce)
Jan 18 Javascript
vue 使用eventBus实现同级组件的通讯
Mar 02 Javascript
javascript获取select值的方法完整实例
Jun 20 Javascript
基于vue、react实现倒计时效果
Aug 26 Javascript
WebWorker 封装 JavaScript 沙箱详情
Nov 02 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
咖啡磨器 如何选购一台适合家用的意式磨豆机
2021/03/05 新手入门
《PHP边学边教》(02.Apache+PHP环境配置――下篇)
2006/12/13 PHP
解析在PHP中使用mysqli扩展库对mysql的操作
2013/07/03 PHP
PHP使用Alexa API获取网站的Alexa排名例子
2014/06/12 PHP
日期函数扩展类Ver0.1.1
2006/09/07 Javascript
Ajax+Json 级联菜单实现代码
2009/10/27 Javascript
JavaScript格式化数字的函数代码
2010/11/30 Javascript
javascript工具库代码
2012/03/29 Javascript
基于jQuery实现左右div自适应高度完全相同的代码
2012/08/09 Javascript
jquery改变tr背景色的示例代码
2013/12/28 Javascript
node.js中的events.emitter.removeAllListeners方法使用说明
2014/12/10 Javascript
jquery实现从数组移除指定的值
2015/06/24 Javascript
JS代码实现table数据分页效果
2016/05/26 Javascript
JavaScript编码风格指南(中文版)
2016/08/26 Javascript
node使用UEditor富文本编辑器的方法实例
2017/07/11 Javascript
Vue计算属性的使用
2017/08/04 Javascript
基于javascript canvas实现五子棋游戏
2020/07/08 Javascript
解决antd 下拉框 input [defaultValue] 的值的问题
2020/10/31 Javascript
JS数据类型分类及常用判断方法
2020/11/19 Javascript
Python简单调用MySQL存储过程并获得返回值的方法
2015/07/20 Python
详解Python 2.6 升级至 Python 2.7 的实践心得
2017/04/27 Python
python opencv判断图像是否为空的实例
2019/01/26 Python
Python实现定制自动化业务流量报表周报功能【XlsxWriter模块】
2019/03/11 Python
Python imageio读取视频并进行编解码详解
2019/12/10 Python
Python本地及虚拟解释器配置过程解析
2020/10/13 Python
房地产融资计划书
2014/01/10 职场文书
幼师自我鉴定
2014/02/01 职场文书
模具毕业生推荐信
2014/02/15 职场文书
年会搞笑主持词
2014/03/27 职场文书
校园标语大全
2014/06/19 职场文书
后进基层党组织整改方案
2014/10/25 职场文书
2014年安全管理工作总结
2014/12/01 职场文书
幼儿园中班教师个人总结
2015/02/05 职场文书
正确使用MySQL update语句
2021/05/26 MySQL
java objectUtils 使用可能会出现的问题
2022/02/28 Java/Android
《乙女游戏世界对路人角色很不友好》OP主题曲无字幕动画MV公开
2022/04/05 日漫