菜鸟初探Vue源码(七)-- 组件注册

223 阅读1分钟

在Vue.js中,定义了一些内置组件(keepAlive、component、transition、transition-group、slot等),可以直接使用。除此之外,如果我们想使用一些自定义组件,必须要先注册组件,否则会报如下错误。

Vue.js 提供了2中组件的注册方式:全局注册和局部注册。接下来我们从源码的角度分析这两种注册方式。

全局注册

使用Vue.component(tagName, options)注册一个全局组件。

export const ASSET_TYPES = ['component','directive','filter'];
ASSET_TYPES.forEach(type => {
    Vue[type] = function (
      id: string,
      definition: Function | Object
    ): Function | Object | void {
      if (!definition) {
        return this.options[type + 's'][id]
      } else {
        if (type === 'component' && isPlainObject(definition)) {
          definition.name = definition.name || id
          // 用Vue.extend将definition转化为构造器
          definition = this.options._base.extend(definition)
        }
        if (type === 'directive' && typeof definition === 'function') {
          definition = { bind: definition, update: definition }
        }
        // 给Vue.options上扩展definition
        this.options[type + 's'][id] = definition
        return definition
      }
    }
})

以上是对Vue.options[components]的扩展,那组件到底是在什么地方注册的呢?

_createElement中判断是我们自定义的标签(非预留标签)时,会调用resolveAsset()对构造器名称处理(比如小驼峰、大驼峰,这也是为什么我们可以使用多种方式命名组件),再调用createComponent生成vnode

export function _createElement (
  context: Component,
  tag?: string | Class<Component> | Function | Object,
  data?: VNodeData,
  children?: any,
  normalizationType?: number
): VNode | Array<VNode> {
  if (typeof tag === 'string') {
    let Ctor
    if (config.isReservedTag(tag)) {
      // platform built-in elements
    } else if ((!data || !data.pre) && isDef(Ctor = resolveAsset(context.$options, 'components', tag))) {
      // component
      vnode = createComponent(Ctor, data, context, children, tag)
    } else {
      // ...
      vnode = new VNode(tag, data, children, undefined, undefined, context)
    }
  }
}
export function resolveAsset (
  options: Object,
  type: string,
  id: string,
  warnMissing?: boolean
): any {
  if (typeof id !== 'string') return;
  const assets = options[type]
  // check local registration variations first
  if (hasOwn(assets, id)) return assets[id]
  const camelizedId = camelize(id)
  if (hasOwn(assets, camelizedId)) return assets[camelizedId]
  const PascalCaseId = capitalize(camelizedId)
  if (hasOwn(assets, PascalCaseId)) return assets[PascalCaseId]
  // fallback to prototype chain
  const res = assets[id] || assets[camelizedId] || assets[PascalCaseId]
  return res
}

局部注册

局部注册是通过Vue.extend方法,代码Sub.options = mergeOptions(Super.options, extendOptions)将配置合并。随后在子组件初始化合并配置时,经过vm.$options = Object.create(vm.constructor.options)将其合并到$options上。