介绍Vue全局注册组件过程之前先大体认识一下Vue对象。这个对象的重要性不言而喻了。其实这个对象是个构造函数对象
function Vue(options){
this._init(options);
}
在new Vue实例的时候会执行_init函数进行一些初始化操作。而这个_init函数是Vue对象原型上的方法,是执行完全局函数initMixin函数后加上的。另外还会执行其他一些全局函数为Vue对象原型加上其他方法。这些全局函数为:
initMixin(Vue), stateMixin(Vue), eventsMixin(Vue), lifecycleMixin(Vue), renderMixin(Vue)
这些是全局函数,加载Vue.js 时就要执行,所以Vue对象原型上有好多方法。例如:
Vue.prototype._init, Vue.prototype.$set, Vue.prototype.$delete, Vue.prototype.$watch, Vue.prototype.$on, Vue.prototype.$once
Vue.prototype.$off, Vue.prototype._update, Vue.prototype._render
言归正传,介绍Vue全局组件注册过程。我们知道全局注册组件用法如下:
Vue.component("comp-name" {
template: "<div>abc</div>",
data: function(){
return {
data1: "global component data"
}
},
methods: {
func1: function(){
console.log("global component func1");
}
}
})
从用法我们看出component 是Vue 上的一个函数,猜测是Vue对象上的方法,或是其原型上的方法。但在Vue.js中没有搜索到此方法。嗯,那怎么办呢,百度一下呗。
原来是在initGlobalAPI 这个全局方法搞的鬼, 接下来就解开这个全局方法真面目。这个函数最后调用了initAssetRegisters方法,进这个函数看下:
ASSET_TYPES.forEach(function (type) {
Vue[type] = function (
id,
definition
) {
if (!definition) {
return this.options[type + 's'][id]
} else {
/* istanbul ignore if */
if (type === 'component') {
validateComponentName(id);
}
if (type === 'component' && isPlainObject(definition)) {
definition.name = definition.name || id;
definition = this.options._base.extend(definition);
}
if (type === 'directive' && typeof definition === 'function') {
definition = { bind: definition, update: definition };
}
this.options[type + 's'][id] = definition;
return definition
}
};
});
ASSET_TYPES 是什么呢?
var ASSET_TYPES = [
'component',
'directive',
'filter'
]
嗯,Vue.component函数找到了。如果你没有找到,那你再找找。 我们来看看这个Vue.component函数体,看它到底干的什么,能注册组件。
首先看这个函数的可接收的参数,从用法上也能看出,第一个是字符串,第二个是对象。从 definition.name = definition.name || id; 这一行代码可以看到如果第二个对象没有name属性,就用第一个参数来做这个组件的名字。这个函数体的关键代码在这一行
definition = this.options._base.extend(definition); 通过这一行返回一个以Vue对象原型为原型的构造函数对象。 那得好好看看这个extend函数。
在initGlobalAPI全局函数中有这么一行Vue.options._base = Vue; 所以能看出extend方法其实是Vue对象上的方法。这个extend方法又在哪里呢? 我们来看看这一行代码initExtend(Vue);
function initExtend (Vue) {
/**
* Each instance constructor, including Vue, has a unique
* cid. This enables us to create wrapped "child
* constructors" for prototypal inheritance and cache them.
*/
Vue.cid = 0;
var cid = 1;
/**
* Class inheritance
*/
Vue.extend = function (extendOptions) {
extendOptions = extendOptions || {};
var Super = this;
var SuperId = Super.cid;
var cachedCtors = extendOptions._Ctor || (extendOptions._Ctor = {});
if (cachedCtors[SuperId]) {
return cachedCtors[SuperId]
}
var name = extendOptions.name || Super.options.name;
if (name) {
validateComponentName(name);
}
var Sub = function VueComponent (options) {
this._init(options);
};
Sub.prototype = Object.create(Super.prototype);
Sub.prototype.constructor = Sub;
Sub.cid = cid++;
Sub.options = mergeOptions(
Super.options,
extendOptions
);
Sub['super'] = Super;
// For props and computed properties, we define the proxy getters on
// the Vue instances at extension time, on the extended prototype. This
// avoids Object.defineProperty calls for each instance created.
if (Sub.options.props) {
initProps$1(Sub);
}
if (Sub.options.computed) {
initComputed$1(Sub);
}
// allow further extension/mixin/plugin usage
Sub.extend = Super.extend;
Sub.mixin = Super.mixin;
Sub.use = Super.use;
// create asset registers, so extended classes
// can have their private assets too.
ASSET_TYPES.forEach(function (type) {
Sub[type] = Super[type];
});
// enable recursive self-lookup
if (name) {
Sub.options.components[name] = Sub;
}
// keep a reference to the super options at extension time.
// later at instantiation we can check if Super's options have
// been updated.
Sub.superOptions = Super.options;
Sub.extendOptions = extendOptions;
Sub.sealedOptions = extend({}, Sub.options);
// cache constructor
cachedCtors[SuperId] = Sub;
return Sub
};
}
extend方法在这里。 下面要剖析一下这个函数看看怎么生成以Vue对象原型为原型的构造函数对象的?
var Sub = function VueComponent (options) {
this._init(options);
};
Sub.prototype = Object.create(Super.prototype);
Sub.prototype.constructor = Sub;
通过这几行代码可以看出这是原型继承。定义构造函数,使构造函数的原型指向到Vue对象的原型上。那么构造函数就具有了和Vue对象一样的原型。当然Vue对象的原型中的各种方法也都具备了。
到这里我们可以看到调用Vue.component方法来注册全局组件其实是生成了一个具备组件属性的Vue对象。其实还没有注册,注册过程在哪呢?看initAssetRegisters 这个方法,注意这一行代码:this.options[type + 's'][id] = definition; 这一行代码就完成注册了。怎么验证已经注册成功呢? 上demo
Vue.component("childComp", {
template: "<div>abc</div>",
data: function(){
return{
data1: "hello, i am child component"
}
},
props: ["parentMsg"],
methods: {
func1 : function(){
}
}
});
for(var key in Vue.options.components){
console.log("register comps are: " + key);
}
你会看到打印:
register comps are: KeepAlive
register comps are: Transition
register comps are: TransitionGroup
register comps are: childComp
为什么还有KeepAlive Transition TransitionGroup 呢,就不多说了。
至此我们已经把注册全局组件过程讲清楚了,你还没看懂,就多看几遍,如果还有哪里没有明白的地方,可以留言,一块探讨一下。注册完之后就要用,怎么用呢?且听下回分解。