# Vue.component

Vue.directiveVue.filterVue.component这三个API在Vue中是在一个函数中实现的功能,这里先拆开分析实现逻辑,本小节最后总结时分析Vue的源码。

# 用法回顾

其用法如下:

Vue.component( id, [definition] )
成功
1
  • 参数

    • {string} id
    • {Function | Object} [definition]
  • 作用

    注册或获取全局组件。注册还会自动使用给定的id设置组件的名称

    // 注册组件,传入一个扩展过的构造器
    Vue.component('my-component', Vue.extend({ /* ... */ }))
    
    // 注册组件,传入一个选项对象 (自动调用 Vue.extend)
    Vue.component('my-component', { /* ... */ })
    
    // 获取注册的组件 (始终返回构造器)
    var MyComponent = Vue.component('my-component')
    
    成功
    1
    2
    3
    4
    5
    6
    7
    8

# 原理分析

从用法回顾中可以知道,该API是用来注册或获取全局组件的,接收两个参数:组件id和组件的定义。 同全局指令一样,注册全局组件是将定义好的组件存放在某个位置,获取组件是根据组件id从存放组件的位置来读取组件。

下面我们就来看一下该API的内部实现原理,其代码如下:

Vue.options = Object.create(null)
// Vue类上添加components属性,用于后续存放组件
Vue.options['components'] = Object.create(null)

Vue.filter = function (id, definition) {
  // 如果没有组件的定义,则为getter,直接返回存放的相关组件
  if (!definition) {
    return this.options['components'][id]
  } else {
    // 如果传入了一个对象,则使用Vue.extend创建为Vue的子类
    if (isPlainObject(definition)) {
      definition.name = definition.name || id
      definition = this.options._base.extend(definition)
    }
    // 存储组件
    this.options['components'][id] = definition
    return definition
  }
}
成功
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

代码逻辑较简单,就是简单的存储和定义二次封装,这里值得一说的是,component较为特殊,如果传入的只是一个对象,则使用Vue.extend创建为Vue的子类,保证后续使用时可以使用Vue相关的方法。

# 回归源码

我们前面说过,Vue.directiveVue.filterVue.component这三个API在Vue中是在同一个函数中实现的,相信大家看过这三个的实现逻辑后,也发现这三者实现很类似。接下来我们看源码是如何实现的

// src/shared/constants.js

export const ASSET_TYPES = [
  'component',
  'directive',
  'filter'
]
成功
1
2
3
4
5
6
7
// src/core/global-api/index.js

ASSET_TYPES.forEach(type => {
  Vue.options[type + 's'] = Object.create(null)
})
成功
1
2
3
4
5
// src/core/global-api/assets.js

ASSET_TYPES.forEach(type => {
  Vue[type] = function (
    id: string,
    definition: Function | Object
  ): Function | Object | void {
    // 不传入定义,则为getter,返回存储的定义
    if (!definition) {
      return this.options[type + 's'][id]
    } else {
      /* istanbul ignore if */
      if (process.env.NODE_ENV !== 'production' && type === 'component') {
        validateComponentName(id)
      }
      // 如果是component,并且definition是个对象,则调用Vue.extend初始化为Vue子类
      if (type === 'component' && isPlainObject(definition)) {
        definition.name = definition.name || id
        definition = this.options._base.extend(definition)
      }
      // 如果是directive,并且definition是个函数
      if (type === 'directive' && typeof definition === 'function') {
        // 默认监听bind和update事件
        definition = { bind: definition, update: definition }
      }
      // 存储定义
      this.options[type + 's'][id] = definition
      return definition
    }
  }
})
成功
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

源码如上,跟我们之前分开分析的一样,就不赘述了。