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 相关文章推荐
ie8本地图片上传预览示例代码
Jan 12 Javascript
JavaScript常用脚本汇总(一)
Mar 04 Javascript
js实现的在线调色板功能完整实例
Dec 21 Javascript
JavaScript基于Dom操作实现查找、修改HTML元素的内容及属性的方法
Jan 20 Javascript
js轮播图的插件化封装详解
Jul 17 Javascript
webpack引入eslint配置详解
Jan 22 Javascript
vue中axios的封装问题(简易版拦截,get,post)
Jun 15 Javascript
angular6.0使用教程之父组件通过url传递id给子组件的方法
Jun 30 Javascript
JS实现可针对算术表达式求值的计算器功能示例
Sep 04 Javascript
vue实现的组件兄弟间通信功能示例
Dec 04 Javascript
详解js中的原型,原型对象,原型链
Jul 16 Javascript
vue封装数字翻牌器
Apr 20 Vue.js
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 获取页面中指定内容的实现类
2014/01/23 PHP
控制打印时页眉角的代码
2007/02/08 Javascript
jQuery设置与获取HTML,文本和值的简单实例
2014/02/26 Javascript
angularjs学习笔记之双向数据绑定
2015/09/26 Javascript
jQuery实现的简单提示信息插件
2015/12/08 Javascript
JSON 对象未定义错误的解决方法
2016/09/29 Javascript
JS实现数组去重方法总结(六种方法)
2017/07/14 Javascript
Vue.js实现的计算器功能完整示例
2018/07/11 Javascript
vue 登录滑动验证实现代码
2018/08/24 Javascript
vue 中引用gojs绘制E-R图的方法示例
2018/08/24 Javascript
服务端预渲染之Nuxt(使用篇)
2019/04/08 Javascript
解决vuex数据异步造成初始化的时候没值报错问题
2019/11/13 Javascript
Node.js中出现未捕获异常的处理方法
2020/06/29 Javascript
微信小程序实现点击生成随机验证码
2020/09/09 Javascript
Linux环境下MySQL-python安装过程分享
2015/02/02 Python
python实现ping的方法
2015/07/06 Python
Python环境下安装使用异步任务队列包Celery的基础教程
2016/05/07 Python
python 计算数组中每个数字出现多少次--“Bucket”桶的思想
2017/12/19 Python
python 输出上个月的月末日期实例
2018/04/11 Python
如何用Python实现简单的Markdown转换器
2018/07/16 Python
对python中if语句的真假判断实例详解
2019/02/18 Python
Python实现账号密码输错三次即锁定功能简单示例
2019/03/29 Python
解决pytorch报错:AssertionError: Invalid device id的问题
2020/01/10 Python
Python中os模块功能与用法详解
2020/02/26 Python
Pandas将列表(List)转换为数据框(Dataframe)
2020/04/24 Python
python神经网络编程实现手写数字识别
2020/05/27 Python
Python 如何实现访问者模式
2020/07/28 Python
python压包的概念及实例详解
2021/02/17 Python
护理毕业生自我鉴定
2014/02/11 职场文书
《小壁虎借尾巴》教学反思
2014/02/16 职场文书
团队经理竞聘书
2014/03/31 职场文书
治超工作实施方案
2014/05/04 职场文书
求职自荐信怎么写
2015/03/04 职场文书
保险公司2016开门红口号集锦
2015/12/24 职场文书
党组织结对共建协议书
2016/03/23 职场文书
python index() 与 rindex() 方法的使用示例详解
2022/12/24 Python