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 相关文章推荐
一个多次搜索+多次传值的解决方案
Jan 20 Javascript
深入理解Javascript闭包 新手版
Dec 28 Javascript
Javascript学习笔记-详解in运算符
Sep 13 Javascript
利用cookie记住背景颜色示例代码
Nov 04 Javascript
简单实现jquery焦点图
Dec 12 Javascript
Angular 4.x 动态创建表单实例
Apr 25 Javascript
Vue波纹按钮组件制作
Apr 30 Javascript
jQuery实现列表的增加和删除功能
Jun 14 jQuery
基于iview的router常用控制方式
May 30 Javascript
浅析vue-cli3配置webpack-bundle-analyzer插件【推荐】
Oct 23 Javascript
vue.js 子组件无法获取父组件store值的解决方式
Nov 08 Javascript
Element InfiniteScroll无限滚动的具体使用方法
Jul 27 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制作简单的内容采集器的原理分析
2008/10/01 PHP
php 实现进制相互转换
2016/04/07 PHP
PHP简单判断iPhone、iPad、Android及PC设备的方法
2016/10/11 PHP
PHP下载远程图片的几种方法总结
2017/04/07 PHP
jQuery计算textarea中文字数(剩余个数)的小程序
2013/11/28 Javascript
浅析Node.js的Stream模块中的Readable对象
2015/07/29 Javascript
js实现文字在按钮上滚动的方法
2015/08/20 Javascript
jquery通过扩展select控件实现支持enter或focus选择的方法
2015/11/19 Javascript
浅析JS操作DOM的一些常用方法
2016/05/13 Javascript
实例讲解Jquery中隐藏hide、显示show、切换toggle的用法
2016/05/13 Javascript
js 连续赋值的简单实现
2016/06/13 Javascript
完美JQuery图片切换效果的简单实现
2016/07/21 Javascript
JS类的定义与使用方法深入探索
2016/11/26 Javascript
Websocket协议详解及简单实例代码
2016/12/12 Javascript
js获取json中key所对应的value值的简单方法
2020/06/17 Javascript
js实现旋转木马效果
2017/03/17 Javascript
浅谈js中的this问题
2017/08/31 Javascript
vue slot 在子组件中显示父组件传递的模板
2018/03/02 Javascript
Layui 数据表格批量删除和多条件搜索的实例
2019/09/04 Javascript
layui上传图片到服务器的非项目目录下的方法
2019/09/26 Javascript
vue-axios同时请求多个接口 等所有接口全部加载完成再处理操作
2020/11/09 Javascript
python 将字符串转换成字典dict
2013/03/24 Python
Django ManyToManyField 跨越中间表查询的方法
2018/12/18 Python
对python中Json与object转化的方法详解
2018/12/31 Python
Python cookie的保存与读取、SSL讲解
2020/02/17 Python
如何使用repr调试python程序
2020/02/28 Python
Python使用sys.exc_info()方法获取异常信息
2020/07/23 Python
详解Python 中的 defaultdict 数据类型
2021/02/22 Python
JavaScript+Canvas实现自定义画板的示例代码
2019/05/13 HTML / CSS
Furla官网:意大利著名的皮革品牌
2019/08/06 全球购物
毕业生自荐信
2013/12/14 职场文书
群众路线四风自我剖析材料
2014/10/08 职场文书
2014年教研工作总结
2014/12/06 职场文书
现实表现材料范文
2014/12/23 职场文书
PostgreSQL13基于流复制搭建后备服务器的方法
2022/01/18 PostgreSQL
漫画《尖帽子的魔法工坊》宣布动画化
2022/04/06 日漫