vue源码探索之组件注册过程

全局注册组件Vue.component

Vue.component('childComp', childComp)

该方法是在初始化导入vue定义,不是实例化定义的。是在定义vue的静态方法initGlobalAPI中初始化的

export function initGlobalAPI (Vue: GlobalAPI) {
  省略。。。
  
  initExtend(Vue); // 定义静态方法Vue.extend
  initAssetRegisters(Vue)
}

全局注册Vue.component的函数定义是在initAssetRegisters中定义

function initAssetRegisters (Vue) {
  /**
   * Create asset registration methods.
   */
   const ASSET_TYPES = [
  'component',
  'directive',
  'filter'
]
  ASSET_TYPES.forEach(function (type) {
    Vue[type] = function (
      id,
      definition
    ) {
      if (!definition) {
        return this.options[type + 's'][id]
      } else {
        /* istanbul ignore if */
        if (process.env.NODE_ENV !== 'production' && type === 'component') {
          validateComponentName(id);
        }
        if (type === 'component' && isPlainObject(definition)) {
          definition.name = definition.name || id;
          // this.options._base就是Vue, Vue.extend就是初始化子类构造器,所以definition就是个构造器
          definition = this.options._base.extend(definition);
        }
        if (type === 'directive' && typeof definition === 'function') {
          definition = { bind: definition, update: definition };
        }
        // 下面代码就是Vue.options.componnets挂载上了全局组件
        this.options[type + 's'][id] = definition;
        return definition
      }
    };
  });
}

image

Vue.extend

而Vue.extend主要是把子类原型指向父类原型,并把大Vue的options,通过合并策略merge到子类构造函数的options

Vue.extend = function (extendOptions: Object): Function {
    extendOptions = extendOptions || {}
    const Super = this
    const SuperId = Super.cid
    const cachedCtors = extendOptions._Ctor || (extendOptions._Ctor = {})
    if (cachedCtors[SuperId]) {
      return cachedCtors[SuperId]
    }

    const name = extendOptions.name || Super.options.name
    if (process.env.NODE_ENV !== 'production' && name) {
      validateComponentName(name)
    }

    const Sub = function VueComponent (options) {
      this._init(options)
    }
    Sub.prototype = Object.create(Super.prototype)
    Sub.prototype.constructor = Sub
    Sub.cid = cid++
    // 合并大Vue上的options到子类构造器上,并且局部注册传入的extendOptions,只绑定在当前子类上,并不是在大Vue上,所以是局部组件
    Sub.options = mergeOptions(
      Super.options,
      extendOptions
    )
    Sub['super'] = Super

    // For props and computed properties, we define the proxy getters on
    // the Vue instances at extension time, on the extended prototype. This
    // avoids Object.defineProperty calls for each instance created.
    if (Sub.options.props) {
      initProps(Sub)
    }
    if (Sub.options.computed) {
      initComputed(Sub)
    }

    // allow further extension/mixin/plugin usage
    Sub.extend = Super.extend
    Sub.mixin = Super.mixin
    Sub.use = Super.use

    // create asset registers, so extended classes
    // can have their private assets too.
    ASSET_TYPES.forEach(function (type) {
      Sub[type] = Super[type]
    })
    // 子组件构造器components中指向自身
    if (name) {
      Sub.options.components[name] = Sub
    }

    
    Sub.superOptions = Super.options
    Sub.extendOptions = extendOptions
    Sub.sealedOptions = extend({}, Sub.options)

    // cache constructor
    cachedCtors[SuperId] = Sub
    return Sub
  }

然后在组件实例化的过程中把构造函数的options.components合并到vm.$options.components上

 Vue.prototype._init = function (options) {
      if (options && options._isComponent) {
      // optimize internal component instantiation
      // since dynamic options merging is pretty slow, and none of the
      // internal component options needs special treatment.
      initInternalComponent(vm, options);
    } else {
      vm.$options = mergeOptions(
        resolveConstructorOptions(vm.constructor),
        options || {},
        vm
      );
    }
 }

然后在创建 vnode 的过程中,会执行 _createElement 方法:

export function _createElement (
  context: Component,
  tag?: string | Class<Component> | Function | Object,
  data?: VNodeData,
  children?: any,
  normalizationType?: number
): VNode | Array<VNode> {
  // ...
  let vnode, ns
  if (typeof tag === 'string') {
    let Ctor
    ns = (context.$vnode && context.$vnode.ns) || config.getTagNamespace(tag)
    // 非html内置标签,会调用 resolveAsset(context.$options, 'components', tag)
    if (config.isReservedTag(tag)) {
      // platform built-in elements
      vnode = new VNode(
        config.parsePlatformTagName(tag), data, children,
        undefined, undefined, context
      )
    } else if (isDef(Ctor = resolveAsset(context.$options, 'components', tag))) {
      // component
      vnode = createComponent(Ctor, data, context, children, tag)
    }
}

resolveAsset

方法很简单,就是先在当前组件的components上查找是否拥有这个组件属性,如果没有就会在components上的原型上查找,返回组件构造器。然后再调createComponent生成组件vnode

function resolveAsset (
  options,
  type,
  id,
  warnMissing
) {
  /* istanbul ignore if */
  if (typeof id !== 'string') {
    return
  }
  var assets = options[type];
  // check local registration variations first
  if (hasOwn(assets, id)) { return assets[id] }
  var camelizedId = camelize(id);
  if (hasOwn(assets, camelizedId)) { return assets[camelizedId] }
  var PascalCaseId = capitalize(camelizedId);
  if (hasOwn(assets, PascalCaseId)) { return assets[PascalCaseId] }
  // fallback to prototype chain
  var res = assets[id] || assets[camelizedId] || assets[PascalCaseId];
  if (process.env.NODE_ENV !== 'production' && warnMissing && !res) {
    warn(
      'Failed to resolve ' + type.slice(0, -1) + ': ' + id,
      options
    );
  }
  return res
}
posted @ 2020-04-14 22:36  raindi  阅读(537)  评论(0编辑  收藏  举报