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 相关文章推荐
ModelDialog JavaScript模态对话框类代码
Apr 17 Javascript
Prototype源码浅析 Number部分
Jan 16 Javascript
javaScript 动态访问JSon元素示例代码
Aug 30 Javascript
jQuery中find()方法用法实例
Jan 07 Javascript
javascript将中国数字格式转换成欧式数字格式的简单实例
Aug 02 Javascript
浅谈jquery之on()绑定事件和off()解除绑定事件
Oct 26 Javascript
jQuery源码分析之init的详细介绍
Feb 13 Javascript
javascript数组去重常用方法实例分析
Apr 11 Javascript
详解刷新页面vuex数据不消失和不跳转页面的解决
Jan 30 Javascript
详解vue 2.6 中 slot 的新用法
Jul 09 Javascript
JS/CSS实现字符串单词首字母大写功能
Sep 03 Javascript
jquery实现手风琴案例
May 04 jQuery
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 截取字符串函数整理(支持gb2312和utf-8)
2010/02/16 PHP
laravel在中间件内生成参数并且传递到控制器中的2种姿势
2019/10/15 PHP
ext监听事件方法[初级篇]
2008/04/27 Javascript
jquery插件之easing 动态菜单
2010/08/21 Javascript
Jquery加载时从后台读取数据绑定到dropdownList实例
2013/06/09 Javascript
javascript 回到顶部效果的实现代码
2014/02/17 Javascript
AngularJS实现动态编译添加到dom中的方法
2016/11/04 Javascript
ajax 提交数据到后台jsp页面及页面跳转问题
2017/01/19 Javascript
常用的9个JavaScript图表库详解
2017/12/19 Javascript
Vue2.5通过json文件读取数据的方法
2018/02/27 Javascript
JavaScript:ES2019 的新特性(译)
2019/08/08 Javascript
nodejs和react实现即时通讯简易聊天室功能
2019/08/21 NodeJs
javascript设计模式之迭代器模式
2020/01/30 Javascript
Vue + Node.js + MongoDB图片上传组件实现图片预览和删除功能详解
2020/04/29 Javascript
Python中使用glob和rmtree删除目录子目录及所有文件的例子
2014/11/21 Python
Python发送email的3种方法
2015/04/28 Python
Python基于回溯法子集树模板解决全排列问题示例
2017/09/07 Python
Python3.6连接Oracle数据库的方法详解
2018/05/18 Python
浅谈Pycharm调用同级目录下的py脚本bug
2018/12/03 Python
Python3爬虫里关于代理的设置总结
2020/07/30 Python
CSS3 网页下拉菜单代码解释 中文翻译
2010/02/27 HTML / CSS
CSS中几个与换行有关的属性简明总结
2014/04/15 HTML / CSS
有750多个顶级品牌的瑞士时尚在线:ABOUT YOU
2017/01/04 全球购物
香港最新科技与优质家居产品购物网站:J SELECT
2018/08/21 全球购物
Spotahome意大利:公寓和房间出租
2020/02/21 全球购物
Farfetch巴西官网:奢侈品牌时尚购物平台
2020/10/19 全球购物
材料成型专业个人求职信范文
2013/09/25 职场文书
党员四风剖析材料
2014/08/27 职场文书
政风行风自查自纠报告
2014/10/21 职场文书
作文批改评语
2014/12/25 职场文书
一年级数学上册复习计划
2015/01/17 职场文书
金榜题名主持词
2015/07/02 职场文书
学校运动会开幕词
2016/03/03 职场文书
创业计划书之溜冰场
2019/10/25 职场文书
Nginx虚拟主机的配置步骤过程全解
2022/03/31 Servers
Nginx的gzip相关介绍
2022/05/11 Servers