• Vue2.0源码理解(6) - 组件注册


    组件注册

    前言

    在 Vue.js 中,除了它内置的组件如 keep-alive、component、transition、transition-group 等,其它用户自定义组件在使用前必须注册。在开发过程中可能会遇到如下报错信息:

    Unknown custom element: <app> - did you register the component correctly? 
    For recursive components, make sure to provide the "name" option.
    

    一般报这个错的原因都是我们使用了未注册的组件。Vue.js 提供了 2 种组件的注册方式,全局注册和局部注册。接下来我们从源码分析的角度来分析这两种注册方式。

    全局注册

    在初始化加载阶段会调用initAssetRegisters函数把需要注册的组件挂载到Vue.options上。

    // src\core\global-api\index.js
    initAssetRegisters(Vue)
    
    // src\core\global-api\assets.js
    export function initAssetRegisters (Vue: GlobalAPI) {
      // 标注①
      ASSET_TYPES.forEach(type => {
        Vue[type] = function (
          id: string,
          definition: Function | Object
        ): Function | Object | void {
          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)) {
              // 优先拿name,没有则取id
              definition.name = definition.name || id
              // 标注②
              definition = this.options._base.extend(definition)
            }
            if (type === 'directive' && typeof definition === 'function') {
              definition = { bind: definition, update: definition }
            }
            this.options[type + 's'][id] = definition
            return definition
          }
        }
      })
    }
    

    标注①:
    对ASSET_TYPES进行遍历,我们先看看遍历对象ASSET_TYPES是什么?

    // src\shared\constants.js
    export const ASSET_TYPES = [
      'component',
      'directive',
      'filter'
    ]
    

    其实就是存放着插件、指令、过滤器这三个分类名称的数组,这里我们只单独针对component进行分析。
    标注②:
    this.options._base其实是Vue,具体原因请查看之前的文章《组件的创建和patch过程》
    通过Vue.extend把对象转换成构造器。
    最后把definition放到this.options即Vue.options上,然后return definition。
    虽然挂载到Vue.options上,但是又是什么时候会被拿去注册成真正的组件呢?
    我们回顾_createElement函数:

    // src\core\vdom\create-element.js
    export function _createElement (
      context: Component,
      tag?: string | Class<Component> | Function | Object,
      data?: VNodeData,
      children?: any,
      normalizationType?: number
    ): VNode | Array<VNode> {
        ...
        if (typeof tag === 'string') {
            //是否HTML原生标签
            if (config.isReservedTag(tag)) {
                ...
            // 标注①:resolveAsset
            } else if ((!data || !data.pre) && isDef(Ctor = resolveAsset(context.$options, 'components', tag))) {
                // component
                vnode = createComponent(Ctor, data, context, children, tag)
            } else {
                ...
            }
        }
    }
    

    标注①:resolveAsset函数做了什么?

    // src\core\util\options.js
    export function resolveAsset (
      options: Object,
      type: string,
      id: string,
      warnMissing?: boolean
    ): any {
      /* istanbul ignore if */
      if (typeof id !== 'string') {
        return
      }
      const assets = options[type]
      //判断配置中是否存在该组件
      if (hasOwn(assets, id)) return assets[id]
      const camelizedId = camelize(id)
      //id转换成驼峰型判断
      if (hasOwn(assets, camelizedId)) return assets[camelizedId]
      const PascalCaseId = capitalize(camelizedId)
      //id转换成首字母大写判断
      if (hasOwn(assets, PascalCaseId)) return assets[PascalCaseId]
      // fallback to prototype chain
    //   原型上面找
      const 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
    }
    

    其实就是经过各种情况判断识别Vue.options是否有定义该组件,有的话则返回,然后最后经过createComponent函数进行了组件的注册。

    局部注册

    局部注册其实和全局注册的几乎一样,只是它需要在此前做一个option合并:

    // src\core\global-api\extend.js
    Sub.options = mergeOptions(
      Super.options,
      extendOptions
    )
    

    关于合并的详细分析请查阅之前文章《合并配置》
    由于合并配置是挂载于Sub上的,也就是说它只是一个在当前Sub作用域下的,一次这种创建方式的组件只能局部使用。

  • 相关阅读:
    Xilinx的TestPattern模块编译错误解决方法
    微信小程序---分包加载
    大数据可视化——Sqoop与Hive的安装详解
    taro小程序开发、h5前端开发有什么不一样呢?
    Linux系统使用iftop查看带宽占用情况
    FineReport可视化数据图表- 函数计算组成和语法示例
    1. Pytorch的基本语法
    微信小程序自定义组件Tabs及会议管理,投票管理与个人中心开发指南
    编写脚本一键安装rsyslog
    FFmpeg 命令:从入门到精通 | ffmpeg 命令转封装
  • 原文地址:https://www.cnblogs.com/YmmY/p/15924537.html