Vue2.0源码学习(6)-组件注册

博客 分享
0 238
张三
张三 2022-02-22 19:55:41
悬赏:0 积分 收藏

Vue2.0源码学习(6) - 组件注册

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.jsinitAssetRegisters(Vue)
// src\core\global-api\assets.jsexport 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.jsexport 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.jsexport 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.jsexport 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.jsSub.options = mergeOptions(  Super.options,  extendOptions)

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

posted @ 2022-02-22 19:09 Inès 阅读(23) 评论(0) 编辑 收藏 举报
回帖
    张三

    张三 (王者 段位)

    821 积分 (2)粉丝 (41)源码

     

    温馨提示

    亦奇源码

    最新会员