Vue如何实现组件的源码解析
官网上关于组件继承分为两大类,全局组件和局部组件。无论哪种方式,最核心的是创建组件,然后根据场景不同注册组件。
有一点要牢记,“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>
主要涉及到两个静态方法:
-
vue.extend
:通过扩展vue实例的方法创建组件 -
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属性,都是由{组件名:组件函数}
构成的,这样在后续使用时,可以直接利用实例内部的组件构建函数创建组件实例。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
上一篇: 微信小程序图片宽100%显示并且不变形
下一篇: vue2.0 自定义日期时间过滤器
推荐阅读
-
vue的props实现子组件随父组件一起变化
-
vue2.0 使用element-ui里的upload组件实现图片预览效果方法
-
Vue.JS实现垂直方向展开、收缩不定高度模块的JS组件
-
说说如何在Vue.js中实现数字输入组件的方法
-
详解VUE里子组件如何获取父组件动态变化的值
-
SpringBoot 源码解析 (七)----- Spring Boot的核心能力 - SpringBoot如何实现SpringMvc的?
-
JSP实用教程之简易文件上传组件的实现方法(附源码)
-
基于vue2的canvas时钟倒计时组件步骤解析
-
基于Vue2实现简易的省市区县三级联动组件效果
-
Vue组件BootPage实现简单的分页功能