问题背景
最近发现通过远程加载组件后,在组件初始化的过程中,浏览器控制台有报错:
于是就跟着代码流程看看报错的原因。定位到报错处的代码:
const originalVal = comp.props[key].default || '';
其中comp是远程加载的Vue单文件组件经过编译之后的发布到CDN上的组件。我们知道一般Vue单文件组件经过vue-loader处理之后一般会变成具有这种数据结构的对象:
整体结构和我们通常的写的组件options比较像似,这个对象作为Vue中比较常见的h函数处理后就能渲染出来了。然而,经过查看本次从远程环境中加载的对象,显示为:
如果对Vue比较熟悉的话,可能一眼就能看出问题所在了。但是由于平时都比较关注Vue的响应式原理、各种接口API了,反倒对Vue整体上的了解有些欠缺了。
Vue组件
要详细理解上述的具体原因,需要从Vue组件进行深入理解。说起Vue组件化,通常都会想起Vue.component, 而对于Vue.extend的使用反而比较少。
Vue.extend原理分析
刚开始见到extend方法时,其实觉得比较奇怪的,后来仔细理解了下,觉得名字挺贴切的。
这里可以参考Version2.0.0版本中,Vue对extend方法的实现。其实,整个extend的流程是比较清晰的(可以参考代码中给出的一点点注释)。
主要是通过Object.create方法实现原型链继承(对于Object.create的用法可以参考之前的一篇文章:链接),返回的Sub是一个构造函数,通过new方式调用后,就会通过调用Vue原型链上的_init方法进行初始化,后续流程和new Vue(options)创建一个Vue实例基本上就一致了。
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;
// 1. 定义extend返回对象
var Sub = function VueComponent (options) {
this._init(options);
};
// 1.1 通过Object.create方法进行原型链继承
Sub.prototype = Object.create(Super.prototype);
Sub.prototype.constructor = Sub;
Sub.cid = cid++;
// 2. 设置构造函数上的options, 用来存储配置
Sub.options = mergeOptions(
Super.options,
extendOptions
);
Sub['super'] = Super;
Sub.extend = Super.extend;
config._assetTypes.forEach(function (type) {
Sub[type] = Super[type];
});
if (name) {
Sub.options.components[name] = Sub;
}
Sub.superOptions = Super.options;
Sub.extendOptions = extendOptions;
if (isFirstExtend) {
extendOptions._Ctor = Sub;
}
// 3. 返回新的构造函数
return Sub
};
Vue.component原理分析
在进行组件注册时,Vue.component是一个使用频率比较高的方法,一般大家对调用方法都比较熟悉了。简单介绍一下Vue 2.0.0版本的实现源码(删除了一些无关component函数里面的分支),便于与Vue.extend进行比较
Vue[type] = function (
id,
definition
) {
if (!definition) {
// 直接获取组件
return this.options[type + 's'][id]
} else {
if (type === 'component' && isPlainObject(definition)) {
definition.name = definition.name || id;
// 定义组件的构造函数
definition = Vue.extend(definition);
}
this.options[type + 's'][id] = definition;
return definition
}
};
从Vue中的代码实现中可以获取以下几点信息:
为什么更改成Vue.extend(options),测试组件渲染还是正常呢?
这是因为Vue在使用h函数(createElement方法)时,对传入的参数进行了判断,然后进行分类处理,其实最终的结果还是转换成组件的构造函数。然而我们自己的代码逻辑中并未对构造函数这种形式的组件进行判断和处理,导致异常的发生。
一般来说有两种解决方法:
- 规范内部对组件的写法,统一成一种格式,这样开发同学在写代码时会比较清晰
- 借鉴Vue的做法,对两种组件写法进行兼容处理
总结
Vue.extend方法创建一个组件的构造函数,通过new调用后可以获得组件的一个实例对象。
stateDiagram-v2
Options --> Vue
Options --> Vue.extend
Options --> Vue.component
Vue.component-->Vue.extend
Vue --> instance
Vue.extend --> instance
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!