深入浅出vue.js---全局API的实现原理----Vue.extend

一、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;
}

 

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值