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 相关文章推荐
对google个性主页的拖拽效果的js的完整注释[转]
Apr 10 Javascript
有趣的JavaScript数组长度问题代码说明
Jan 20 Javascript
Jquery写一个鼠标拖动效果实现原理与代码
Dec 24 Javascript
javascript调试过程中找不到哪里出错的可能原因
Dec 16 Javascript
jQuery中extend函数详解
Feb 13 Javascript
Prototype框架详解
Nov 25 Javascript
JS实现上下左右对称的九九乘法表
Feb 22 Javascript
微信小程序实战之运维小项目
Jan 17 Javascript
详解webpack 如何集成第三方js库
Jun 29 Javascript
JavaScript递归算法生成树形菜单
Aug 15 Javascript
javascript 内存模型实例详解
Apr 18 Javascript
Vue实现省市区三级联动
Dec 27 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
Thinkphp模板中截取字符串函数简介
2014/06/17 PHP
PHP解压tar.gz格式文件的方法
2016/02/14 PHP
PHP+HTML+JavaScript+Css实现简单爬虫开发
2016/03/28 PHP
PHP实现bitmap位图排序与求交集的方法
2016/07/28 PHP
PHP类相关知识点实例总结
2016/09/28 PHP
PHP实现双链表删除与插入节点的方法示例
2017/11/11 PHP
TNC vs BOOM BO3 第二场2.13
2021/03/10 DOTA
javascript css styleFloat和cssFloat
2010/03/15 Javascript
Javascript 鼠标移动上去小三角形滑块缓慢跟随效果
2013/04/26 Javascript
浏览器打开层自动缓慢展开收缩实例代码
2013/07/04 Javascript
判断及设置浏览器全屏模式
2014/04/20 Javascript
jQuery Validate表单验证插件 添加class属性形式的校验
2016/01/18 Javascript
原生JavaScript实现滚动条效果
2020/03/24 Javascript
使用jquery.qrcode.min.js实现中文转化二维码
2016/03/11 Javascript
Vue数据驱动模拟实现3
2017/01/11 Javascript
JQueryMiniUI按照时间进行查询的实现方法
2017/06/07 jQuery
inner join 内联与left join 左联的实例代码
2017/09/18 Javascript
js实现复制功能(多种方法集合)
2018/01/06 Javascript
Layer弹出层动态获取数据的方法
2018/08/20 Javascript
vue-cli的build的文件夹下没有dev-server.js文件配置mock数据的方法
2019/04/17 Javascript
ios中视频的最后一桢问题解决
2019/05/14 Javascript
vue 导航内容设置选中状态样式的例子
2019/11/01 Javascript
Vue 实现可视化拖拽页面编辑器
2021/02/01 Vue.js
使用Python3 编写简单信用卡管理程序
2016/12/21 Python
Python列表切片操作实例总结
2019/02/19 Python
Django框架之中间件MiddleWare的实现
2019/12/30 Python
pytorch cuda上tensor的定义 以及减少cpu的操作详解
2020/06/23 Python
css3实现椭圆轨迹旋转的示例代码
2018/10/29 HTML / CSS
军神教学反思
2014/02/04 职场文书
迟到早退检讨书
2014/02/10 职场文书
软件项目开发计划书
2014/05/01 职场文书
美术第二课堂活动总结
2014/07/08 职场文书
总经理岗位职责说明书
2014/07/30 职场文书
解放思想大讨论活动心得体会
2014/09/11 职场文书
女性励志书籍推荐
2019/08/19 职场文书
生命的关键成分来自太空?陨石说是的
2022/04/29 数码科技