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 相关文章推荐
用cookies实现的可记忆的样式切换效果代码下载
Dec 24 Javascript
jquery中ajax学习笔记4
Oct 16 Javascript
JS获取几种URL地址的方法小结
Feb 26 Javascript
使用AngularJS中的SCE来防止XSS攻击的方法
Jun 18 Javascript
jQuery实现右下角可缩放大小的层完整实例
Jun 20 Javascript
关于js二维数组和多维数组的定义声明(详解)
Oct 02 Javascript
真正好用的js验证上传文件大小的简单方法
Oct 27 Javascript
js编写简单的聊天室功能
Aug 17 Javascript
bootstrap模态框弹出和隐藏,动态改变中间内容的实例
Aug 10 Javascript
vue服务端渲染添加缓存的方法
Sep 18 Javascript
jQuery事件模型默认行为执行顺序及trigger()与 triggerHandler()比较实例分析
Apr 30 jQuery
vue实现购物车结算功能
Jun 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
CI框架中libraries,helpers,hooks文件夹详细说明
2014/06/10 PHP
UTF-8正则表达式如何匹配汉字
2015/08/03 PHP
jquery 获取json数据实现代码
2009/04/27 Javascript
js注意img图片的onerror事件的分析
2011/01/01 Javascript
利用jQuery操作对象数组的实现代码
2011/04/27 Javascript
输入密码检测大写是否锁定js实现代码
2012/12/03 Javascript
jQuery往textarea中光标所在位置插入文本的方法
2015/06/26 Javascript
Bootstrap表单布局样式源代码
2016/07/04 Javascript
JS判断日期格式是否合法的简单实例
2016/07/11 Javascript
vue.js学习之UI组件开发教程
2017/07/03 Javascript
AngularJS实用基础知识_入门必备篇(推荐)
2017/07/10 Javascript
IScroll那些事_当内容不足时下拉刷新的解决方法
2017/07/18 Javascript
JavaScript中创建原子的方法总结
2018/08/26 Javascript
Vue-router 切换组件页面时进入进出动画方法
2018/09/01 Javascript
Vue页面跳转动画效果的实现方法
2018/09/23 Javascript
angular inputNumber指令输入框只能输入数字的实现
2019/12/03 Javascript
jQuery编写QQ简易聊天框
2020/08/27 jQuery
[40:48]DOTA2上海特级锦标赛D组败者赛 Liquid VS COL第二局
2016/02/28 DOTA
python错误处理详解
2014/09/28 Python
python抓取百度首页的方法
2015/05/19 Python
Python通过调用有道翻译api实现翻译功能示例
2018/07/19 Python
在Python dataframe中出生日期转化为年龄的实现方法
2018/10/20 Python
Python图像的增强处理操作示例【基于ImageEnhance类】
2019/01/03 Python
Python3模拟登录操作实例分析
2019/03/12 Python
python实现IOU计算案例
2020/04/12 Python
基于Keras中Conv1D和Conv2D的区别说明
2020/06/19 Python
Python利用socket模块开发简单的端口扫描工具的实现
2021/01/27 Python
Python tkinter实现日期选择器
2021/02/22 Python
HTML5重塑Web世界它将如何改变互联网
2012/12/17 HTML / CSS
塑料制成的可水洗的编织平底鞋和鞋子:Rothy’s
2018/09/16 全球购物
行政执法队伍作风整顿个人剖析材料
2014/10/11 职场文书
机关党员三严三实心得体会
2014/10/13 职场文书
二手房购房意向书
2015/05/09 职场文书
返乡农民工证明
2015/06/24 职场文书
JavaScript 防篡改对象的用法示例
2021/04/24 Javascript
Apache Hudi数据布局黑科技降低一半查询时间
2022/03/31 Servers