zoukankan      html  css  js  c++  java
  • 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
    }
    
  • 相关阅读:
    shell 知识点
    folder,source folder,package 区别
    meta viewport 理解
    一张图看懂JavaScript中数组的迭代方法:forEach、map、filter、reduce、every、some
    java 报错及解决
    Charles :mac上的手机代理
    关联本地文件夹到github项目
    tomcat 安装
    Refused to display '[url]' in a frame because it set 'X-Frame-Options' to 'Deny'.
    linux 知识点
  • 原文地址:https://www.cnblogs.com/raind/p/12701872.html
Copyright © 2011-2022 走看看