12.3 内置组件

最后说说Vue思想中的另一个概念,内置组件,其实vue的官方文档有对内置组件进行了列举,分别是component, transition, transition-group, keep-alive, slot,其中<slot>我们在插槽这一节已经详细介绍过,而component的使用这一节也花了大量的篇幅从使用到原理进行了分析。然而学习了slot,component之后,我开始意识到slotcomponent并不是真正的内置组件。内置组件是已经在源码初始化阶段就全局注册好的组件。<slot><component>并没有被当成一个组件去处理,因此也没有组件的生命周期。slot只会在render函数阶段转换成renderSlot函数进行处理,而component也只是借助is属性将createElement的第一个参数从字符串转换为变量,仅此而已。因此重新回到概念的理解,内置组件是源码自身提供的组件,所以这一部分内容的重点,会放在内置组件是什么时候注册的,编译时有哪些不同这两个问题上来。这一部分只是一个抛砖引玉,接下来会有两篇文章专门详细介绍keep-alive,transition, transition-group的实现原理。

12.3.1 构造器定义组件

Vue初始化阶段会在构造器的components属性添加三个组件对象,每个组件对象的写法和我们在自定义组件过程的写法一致,有render函数,有生命周期,也会定义各种数据。

  1. // keep-alive组件选项
  2. var KeepAlive = {
  3. render: function() {}
  4. }
  5. // transition 组件选项
  6. var Transition = {
  7. render: function() {}
  8. }
  9. // transition-group 组件选项
  10. var TransitionGroup = {
  11. render: function() {},
  12. methods: {},
  13. ···
  14. }
  15. var builtInComponents = {
  16. KeepAlive: KeepAlive
  17. };
  18. var platformComponents = {
  19. Transition: Transition,
  20. TransitionGroup: TransitionGroup
  21. };
  22. // Vue构造器的选项配置,compoents选项合并
  23. extend(Vue.options.components, builtInComponents);
  24. extend(Vue.options.components, platformComponents);

extend方法我们在系列的开头,分析选项合并的时候有说过,将对象上的属性合并到源对象中,属性相同则覆盖。

  1. // 将_from对象合并到to对象,属性相同时,则覆盖to对象的属性
  2. function extend (to, _from) {
  3. for (var key in _from) {
  4. to[key] = _from[key];
  5. }
  6. return to
  7. }

最终Vue构造器拥有了三个组件的配置选项。

  1. Vue.components = {
  2. keepAlive: {},
  3. transition: {},
  4. transition-group: {},
  5. }

12.3.2 注册内置组件

仅仅有定义是不够的。组件需要被全局使用还得进行全局的注册,这其实在深入剖析Vue源码 - 选项合并(下)已经阐述清楚了。Vue实例在初始化过程中,最重要的第一步是进行选项的合并,而像内置组件这些资源类选项会有专门的选项合并策略,最终构造器上的组件选项会以原型链的形式注册到实例的compoonents选项中(指令和过滤器同理)。

  1. // 资源选项
  2. var ASSET_TYPES = [
  3. 'component',
  4. 'directive',
  5. 'filter'
  6. ];
  7. // 定义资源合并的策略
  8. ASSET_TYPES.forEach(function (type) {
  9. strats[type + 's'] = mergeAssets; // 定义默认策略
  10. });
  11. function mergeAssets (parentVal,childVal,vm,key) {
  12. var res = Object.create(parentVal || null); // 以parentVal为原型创建一个空对象
  13. if (childVal) {
  14. assertObjectType(key, childVal, vm); // components,filters,directives选项必须为对象
  15. return extend(res, childVal) // 子类选项赋值给空对象
  16. } else {
  17. return res
  18. }
  19. }

关键的两步一个是var res = Object.create(parentVal || null);,它会以parentVal为原型创建一个空对象,最后是通过extend将用户自定义的component选项复制到空对象中。选项合并之后,内置组件也因此在全局完成了注册。

  1. {
  2. components: {
  3. child1,
  4. __proto__: {
  5. keepAlive: {},
  6. transition: {},
  7. transitionGroup: {}
  8. }
  9. }
  10. }

最后我们看看内置组件对象中并没有template模板,而是render函数,除了减少了耗性能的模板解析过程,我认为重要的原因是内置组件并没有渲染的实体。最后的最后,让我们一起期待后续对keep-alivetransition的原理分析,敬请期待。