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中cookie的使用详细分析
May 28 Javascript
使用jQuery简化Ajax开发 Ajax开发入门
Oct 14 Javascript
jquery 操作单选框,复选框,下拉列表实现代码
Oct 27 Javascript
js网页侧边随页面滚动广告效果实现
Apr 14 Javascript
读jQuery之一(对象的组成)
Jun 11 Javascript
js string 转 int 注意的问题小结
Aug 15 Javascript
js调用iframe实现打印页面内容的方法
Mar 04 Javascript
JS实现不使用图片仿Windows右键菜单效果代码
Oct 22 Javascript
微信小程序 wxapp画布 canvas详细介绍
Oct 31 Javascript
Nautil 中使用双向数据绑定的实现
Oct 02 Javascript
前端性能优化建议
Sep 17 Javascript
Ant Design的可编辑Tree的实现操作
Oct 31 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中extract()函数的妙用分析
2012/07/11 PHP
linux实现php定时执行cron任务详解
2013/12/24 PHP
php多进程模拟并发事务产生的问题小结
2018/12/07 PHP
laravel unique验证、确认密码confirmed验证以及密码修改验证的方法
2019/10/16 PHP
JS版网站风格切换实例代码
2008/10/06 Javascript
Javascript writable特性介绍
2015/02/27 Javascript
javascript获取网页各种高宽及位置的方法总结
2016/07/27 Javascript
BootStrap中的表单大全
2016/09/07 Javascript
jQuery通过ajax快速批量提交表单数据
2016/10/25 Javascript
AngularJS表格添加序号的方法
2017/03/03 Javascript
JS+CSS实现网页加载中的动画效果
2017/10/27 Javascript
简述Angular 5 快速入门
2017/11/04 Javascript
element-ui循环显示radio控件信息的方法
2018/08/24 Javascript
jQuery实现模拟搜索引擎的智能提示功能简单示例
2019/01/27 jQuery
vue+php实现的微博留言功能示例
2019/03/16 Javascript
Angular封装搜索框组件操作示例
2019/04/25 Javascript
vue 地区选择器v-distpicker的常用功能
2019/07/23 Javascript
微信小程序利用云函数获取手机号码
2019/12/17 Javascript
[49:42]DOTA2上海特级锦标赛主赛事日 - 3 胜者组第二轮#2Secret VS EG第一局
2016/03/04 DOTA
py2exe 编译ico图标的代码
2013/03/08 Python
python基础教程之Filter使用方法
2017/01/17 Python
Python操作使用MySQL数据库的实例代码
2017/05/25 Python
详解python 3.6 安装json 模块(simplejson)
2019/04/02 Python
python3获取url文件大小示例代码
2019/09/18 Python
python调用Matplotlib绘制分布点图
2019/10/18 Python
windows10 pycharm下安装pyltp库和加载模型实现语义角色标注的示例代码
2020/05/07 Python
python 如何上传包到pypi
2020/12/24 Python
美术专业学生个人自我评价
2013/09/19 职场文书
美德好少年主要事迹
2014/01/29 职场文书
委托书样本
2014/04/02 职场文书
《陈毅探母》教学反思
2014/05/01 职场文书
公司募捐倡议书
2014/05/14 职场文书
庆七一宣传标语
2014/10/08 职场文书
优秀教研组申报材料
2014/12/26 职场文书
2015年机械设备管理工作总结
2015/05/04 职场文书
防止web项目中的SQL注入
2021/12/06 MySQL