12.3 内置组件
最后说说Vue
思想中的另一个概念,内置组件,其实vue
的官方文档有对内置组件进行了列举,分别是component, transition, transition-group, keep-alive, slot
,其中<slot>
我们在插槽这一节已经详细介绍过,而component
的使用这一节也花了大量的篇幅从使用到原理进行了分析。然而学习了slot,component
之后,我开始意识到slot
和component
并不是真正的内置组件。内置组件是已经在源码初始化阶段就全局注册好的组件。而<slot>
和<component>
并没有被当成一个组件去处理,因此也没有组件的生命周期。slot
只会在render
函数阶段转换成renderSlot
函数进行处理,而component
也只是借助is
属性将createElement
的第一个参数从字符串转换为变量,仅此而已。因此重新回到概念的理解,内置组件是源码自身提供的组件,所以这一部分内容的重点,会放在内置组件是什么时候注册的,编译时有哪些不同这两个问题上来。这一部分只是一个抛砖引玉,接下来会有文章专门详细介绍keep-alive,transition, transition-group
的实现原理。
12.3.1 构造器定义组件
Vue
初始化阶段会在构造器的components
属性添加三个组件对象,每个组件对象的写法和我们在自定义组件过程的写法一致,有render
函数,有生命周期,也会定义各种数据。
// keep-alive组件选项
var KeepAlive = {
render: function() {}
}
// transition 组件选项
var Transition = {
render: function() {}
}
// transition-group 组件选项
var TransitionGroup = {
render: function() {},
methods: {},
···
}
var builtInComponents = {
KeepAlive: KeepAlive
};
var platformComponents = {
Transition: Transition,
TransitionGroup: TransitionGroup
};
// Vue构造器的选项配置,compoents选项合并
extend(Vue.options.components, builtInComponents);
extend(Vue.options.components, platformComponents);
extend
方法我们在系列的开头,分析选项合并的时候有说过,将对象上的属性合并到源对象中,属性相同则覆盖。
// 将_from对象合并到to对象,属性相同时,则覆盖to对象的属性
function extend (to, _from) {
for (var key in _from) {
to[key] = _from[key];
}
return to
}
最终Vue
构造器拥有了三个组件的配置选项。
Vue.components = {
keepAlive: {},
transition: {},
transition-group: {},
}
12.3.2 注册内置组件
仅仅有定义是不够的。组件需要被全局使用还得进行全局的注册,这其实在选项合并章节已经阐述清楚了。Vue实例在初始化过程中,最重要的第一步是进行选项的合并,而像内置组件这些资源类选项会有专门的选项合并策略,最终构造器上的组件选项会以原型链的形式注册到实例的compoonents
选项中(指令和过滤器同理)。
// 资源选项
var ASSET_TYPES = [
'component',
'directive',
'filter'
];
// 定义资源合并的策略
ASSET_TYPES.forEach(function (type) {
strats[type + 's'] = mergeAssets; // 定义默认策略
});
function mergeAssets (parentVal,childVal,vm,key) {
var res = Object.create(parentVal || null); // 以parentVal为原型创建一个空对象
if (childVal) {
assertObjectType(key, childVal, vm); // components,filters,directives选项必须为对象
return extend(res, childVal) // 子类选项赋值给空对象
} else {
return res
}
}
关键的两步一个是var res = Object.create(parentVal || null);
,它会以parentVal
为原型创建一个空对象,最后是通过extend
将用户自定义的component
选项复制到空对象中。选项合并之后,内置组件也因此在全局完成了注册。
{
components: {
child1,
__proto__: {
keepAlive: {},
transition: {},
transitionGroup: {}
}
}
}
最后我们看看内置组件对象中并没有template
模板,而是render
函数,除了减少了耗性能的模板解析过程,我认为重要的原因是内置组件并没有渲染的实体。最后的最后,让我们一起期待后续对keep-alive
原理分析,敬请期待。
12.4 小结
这节我们详细的介绍了动态组件的原理,我们经常使用<component :is="">
以达到不同组件切换的目的,实际上是由于is
这个关键字让模板编译成render
函数时,组件render
的标签是变量,这样在渲染阶段,随着数据的不同会渲染不同的组件。动态组件还有一种用法是使用内联模板去访问子组件的数据,这又增加了一种子父组件通信的方法。但是官方并不建议我们这样做,因为内联模板会让作用域变得混乱。内联组件实现父子通信的原理是它让父组件的编译过程放到了子组件,这样顺利成章的父组件就可以访问到子组件的变量。文章的最后引出了内置组件,Vue
中真正的内置组件只有keep-alive, transition, transition-group
三种,他们本质上是在内部定义好组件选项,并进行全局注册。下一节,我们将进入keep-alive
内置组件的深度分析。