一、Vue.extend
Vue.extend( optons );
(1)参数
{ object } options
(2)用法
使用基础Vue构造器创建一个“子类”,其参数是一个包含“组件选项”的对象。data选项是特例,在Vue.extend()中,它必须是函数。
<div id="mount-point"></div>
<!-- 创建构造器 -->
var Profile = Vue.extend({
template:'<p>{{firstName}} {{lastName}} aka{{alias}}</p>',
data:function(){
return{
firstName:'Walter',
lastName:'White',
alias:'Heisenberg'
}
}
})
<!-- 创建Profile实例,并挂载到一个元素上 -->
new Profile().$mount('#mount-point');
(3)全局API和示例方法不同,后者是在Vue的原型上挂载方法,也就是在Vue.prototype上挂载方法,而前者是直接在Vue上挂载方法。
Vue.extend = function(extendOptions){
<!-- 做点什么 -->
}
(4)作用
Vue.extend的作用是创建一个子类,所以可以创建一个子类,然后让它继承Vue身上的一些功能。
二、实现
(1)创建一个子类
let cid = 1;
Vue.extend = function(extendOptions){
extendOptions = extendOptions || {};
const Super = this;
const SuperId = Super.cid;
const cachedCtors = extendOptions._Ctor || (extendOptions._Ctor = {});
if(cachedCtors[SuperId]){
return cachedCtors[SuperId];
}
const name = extendOptions.name || Super.options.name;
if(process.env.NODE_ENV !== 'production'){
if(!/^[a-zA-Z][\w-]*$/.test(name)){
warn(
'invalid component name:"'+name+'".Component names'+
'can only contain alphanumeric characters and the hyphen,'+
'and must start with a letter.'
)
}
}
const Sub = function VueComponent(options){
this._init(options);
}
<!-- 缓存构造函数 -->
cachedCtors[SuperId] = Sub;
return sub;
}
1、为了性能考虑,在Vue.extend方法内增加了缓存策略。反复调用Vue.extend其实应该返回同一个结果。
2、只要返回结果是固定的,就可以将计算结果缓存,再次调用extend方法时,只需要从缓存中取出结果即可。
3、使用父类的id作为缓存的key,将子类缓存在cachedCtors中。
4、对name的校验,如果发现name选项不合格,会在开发环境下发出警告。
5、最后,创建子类并将它返回,这一步并没有继承的逻辑,此时子类是不能用的,它还不具备Vue的能力。
(2)子类继承Vue的能力
- 首先,将父类的原型继承到子类中
Sub.prototype = Object.create(Super.prototype);
Sub.prototype.constructor = Sub;
Sub.cid = cid++;
1、为子类添加了cid,它表示每个类的唯一标识。
- 将父类的options选项继承到子类中
Sub.options = mergeOptions(
Super.options,
extendOptions
)
Sub['super'] = Super;
1、合并了父类选项与子类选项的逻辑,并将父类保存到子类的super属性中。而mergeOptions方法会将两个选项合并为一个新对象。
- 如果选项中存在props属性,则初始化它
if(Sub.options.props){
initProps(Sub);
}
1、初始化props的作用是将key代理到_props中。例如,vm.name实际上可以访问到的是Sub.prototype._props.name。
function initProps(Comp){
const props = Comp.options.props;
for(const key in props){
proxy(Comp.prototype,'_props',key)
}
}
function proxy(target,sourceKey,key){
sharedPropertyDefinition.get = function proxyGetter(){
return this[sourceKey][key];
}
sharedPropertyDefinition.set = function proxySetter(val){
this[sourceKey][key] = val;
}
Object.definedProperty(target,key,sharedPropertyDefinition);
}
- 如果选项中存在computed,则对它进行初始化
if(Sub.options.computed){
initComputed(Sub);
}
function initComputed (Comp){
const computed = Comp.options.computed;
for(const key in computed){
defineComputed(Comp.prototype,key,computed[key]);
}
}
- 将父类中存在的属性依次复制到子类中
Sub.extend = Super.extend;
Sub.mixin = Super.mixin'
Sub.use = Super.use;
<!-- ASSET_TYPES = ['component','directive','filter'] -->
ASSET_TYPES.forEach(function(type){
Sub[type] = Super[type];
})
if(name){
Sub.options.components[name] = Sub;
}
Sub.superOptions = Super.options;
Sub.extendOptions = extendOptions;
Sub.sealedOptions = extend({},Sub.options);
1、复制到子类中的方法包括extend、mixin、use、component、directive和filter
2、在子类上新增了superOptions、extendOptions和sealedOptions属性
- 总结
创建了一个Sub函数并继承了父级。如果直接使用Vue.extend,则Sub继承于Vue构造函数。
三、完整代码
let cid = 1;
Vue.extend = function(extendOptions){
extendOptions = extendOptions || {};
const Super = this;
const SuperId = Super.cid;
const cachedCtors = extendOptions._Ctor || (extendOptions._Ctor = {});
if(cachedCtors[SuperId]){
return cachedCtors[SuperId];
}
const name = extendOptions.name || Super.options.name;
if(process.env.NODE_ENV !== 'production'){
if(!/^[a-zA-Z][\w-]*$/.test(name)){
warn(
'invalid component name:"'+name+'".Component names'+
'can only contain alphanumeric characters and the hyphen,'+
'and must start with a letter.'
)
}
}
const Sub = function VueComponent(options){
this._init(options);
}
<!-- 将父类原型继承到子类中 -->
<!-- cid每个类的唯一标识 -->
Sub.prototype = Object.create(Super.prototype);
Sub.prototype.constructor = Sub;
Sub.cid = cid++;
<!-- 将父类的options选项继承到子类中 -->
Sub.options = mergeOptions(
Super.options,
extendOptions
)
Sub['super'] = Super;
<!-- 如果选项中存在props属性,则初始化它 -->
if(Sub.options.props){
initProps(Sub);
}
<!-- 如果选项中存在computed属性,则对它进行初始化 -->
if(Sub.options.computed){
initComputed(Sub);
}
<!-- 将父类中存在的属性依次复制到子类中 -->
Sub.extend = Super.extend;
Sub.mixin = Super.mixin'
Sub.use = Super.use;
<!-- ASSET_TYPES = ['component','directive','filter'] -->
ASSET_TYPES.forEach(function(type){
Sub[type] = Super[type];
})
if(name){
Sub.options.components[name] = Sub;
}
Sub.superOptions = Super.options;
Sub.extendOptions = extendOptions;
Sub.sealedOptions = extend({},Sub.options);
<!-- 缓存构造函数 -->
cachedCtors[SuperId] = Sub;
return sub;
}