5.1 组件两种注册方式

熟悉Vue开发流程的都知道,Vue组件在使用之前需要进行注册,而注册的方式有两种,全局注册和局部注册。在进入源码分析之前,我们先回忆一下两者的用法,以便后续掌握两者的差异。

5.1.1 全局注册

  1. Vue.component('my-test', {
  2. template: '<div>{{test}}</div>',
  3. data () {
  4. return {
  5. test: 1212
  6. }
  7. }
  8. })
  9. var vm = new Vue({
  10. el: '#app',
  11. template: '<div id="app"><my-test><my-test/></div>'
  12. })

其中组件的全局注册需要在全局实例化Vue前调用,注册之后可以用在任何新创建的Vue实例中调用。

5.1.2 局部注册

  1. var myTest = {
  2. template: '<div>{{test}}</div>',
  3. data () {
  4. return {
  5. test: 1212
  6. }
  7. }
  8. }
  9. var vm = new Vue({
  10. el: '#app',
  11. component: {
  12. myTest
  13. }
  14. })

当只需要在某个局部用到某个组件时,可以使用局部注册的方式进行组件注册,此时局部注册的组件只能在注册该组件内部使用。

5.1.3 注册过程

在简单回顾组件的两种注册方式后,我们来看注册过程到底发生了什么,我们以全局组件注册为例。它通过Vue.component(name, {...})进行组件注册,Vue.component是在Vue源码引入阶段定义的静态方法。

  1. // 初始化全局api
  2. initAssetRegisters(Vue);
  3. var ASSET_TYPES = [
  4. 'component',
  5. 'directive',
  6. 'filter'
  7. ];
  8. function initAssetRegisters(Vue){
  9. // 定义ASSET_TYPES中每个属性的方法,其中包括component
  10. ASSET_TYPES.forEach(function (type) {
  11. // type: component,directive,filter
  12. Vue[type] = function (id,definition) {
  13. if (!definition) {
  14. // 直接返回注册组件的构造函数
  15. return this.options[type + 's'][id]
  16. }
  17. ...
  18. if (type === 'component') {
  19. // 验证component组件名字是否合法
  20. validateComponentName(id);
  21. }
  22. if (type === 'component' && isPlainObject(definition)) {
  23. // 组件名称设置
  24. definition.name = definition.name || id;
  25. // Vue.extend() 创建子组件,返回子类构造器
  26. definition = this.options._base.extend(definition);
  27. }
  28. // 为Vue.options 上的component属性添加将子类构造器
  29. this.options[type + 's'][id] = definition;
  30. return definition
  31. }
  32. });
  33. }

Vue.components有两个参数,一个是需要注册组件的组件名,另一个是组件选项,如果第二个参数没有传递,则会直接返回注册过的组件选项。否则意味着需要对该组件进行注册,注册过程先会对组件名的合法性进行检测,要求组件名不允许出现非法的标签,包括Vue内置的组件名,如slot, component等。

  1. function validateComponentName(name) {
  2. if (!new RegExp(("^[a-zA-Z][\\-\\.0-9_" + (unicodeRegExp.source) + "]*$")).test(name)) {
  3. // 正则判断检测是否为非法的标签
  4. warn(
  5. 'Invalid component name: "' + name + '". Component names ' +
  6. 'should conform to valid custom element name in html5 specification.'
  7. );
  8. }
  9. // 不能使用Vue自身自定义的组件名,如slot, component,不能使用html的保留标签,如 h1, svg等
  10. if (isBuiltInTag(name) || config.isReservedTag(name)) {
  11. warn(
  12. 'Do not use built-in or reserved HTML elements as component ' +
  13. 'id: ' + name
  14. );
  15. }
  16. }

在经过组件名的合法性检测后,会调用extend方法为组件创建一个子类构造器,此时的this.options._base代表的就是Vue构造器。extend方法的定义在介绍选项合并章节有重点介绍过,它会基于父类去创建一个子类,此时的父类是Vue,并且创建过程子类会继承父类的方法,并会和父类的选项进行合并,最终返回一个子类构造器。

代码处还有一个逻辑,Vue.component()默认会把第一个参数作为组件名称,但是如果组件选项有name属性时,name属性值会将组件名覆盖。

总结起来,全局注册组件就是Vue实例化前创建一个基于Vue的子类构造器,并将组件的信息加载到实例options.components对象中。

接下来自然而然会想到一个问题,局部注册和全局注册在实现上的区别体现在哪里?我们不急着分析局部组件的注册流程,先以全局注册的组件为基础,看看作为组件,它的挂载流程有什么不同。