Vue source code learning-componentization (5) component registration

Vue source code learning-componentization (5) component registration

学习内容和文章内容来自 黄轶老师
黄轶老师的慕课网视频教程地址:"Vue.js2.0 Source Code Revealed" ,
黄轶老师拉钩教育教程地址:"Vue.js 3.0 Core Source Code Analysis"
这里分析的源码是Runtime + Compiler 的 Vue.js
调试代码在:node_modules\vue\dist\vue.esm.js 里添加
vue版本:Vue.js 2.5.17-beta

你越是认真生活,你的生活就会越美好——Frank Lloyd Wright
"The Fruit of Life" Classic Quotations

Click to go back to the complete list of Vue source code learning

Component registration

In Vue.js, in addition to its built-in components such as keep-alive, component, transition, transition-groupetc., other user-defined component must be registered before use. Many students may encounter the following error messages during the development process:

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

The general reason for this error is that we use unregistered components. Vue.js provides two ways to register components, global registration and partial registration. Next, we analyze these two registration methods from the perspective of source code analysis.

Global registration

To register a global component, you can use it Vue.component(tagName, options). E.g:

Vue.component('my-component', {
    
    
  // 选项
})
// src/main.js
// 全局注册 Vue.component('app', App)
import Vue from 'vue'
import App from './App.vue'

Vue.component('app', App)
new Vue({
    
    
  el: '#app',
  template: '<app></app>'
})

// src/App.vue 不变
// src\components\HelloWorld.vue 不变

So, Vue.componentthe function is when the definition of it, which is defined as the process occurs at 最开始初始化 Vue 的全局函数the time of the code in src/core/global-api/assets.jsthis case has not been performed in the new Vue :( ())
Insert picture description here

import {
    
     ASSET_TYPES } from 'shared/constants'
import {
    
     isPlainObject, validateComponentName } from '../util/index'

export function initAssetRegisters (Vue: GlobalAPI) {
    
    
  /**
   * Create asset registration methods.
   */
  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)) {
    
    
          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
      }
    }
  })
}

Function first traversal ASSET_TYPES, to give typeafter loading onto Vue. ASSET_TYPESDefined in src/shared/constants.jsthe:

export const ASSET_TYPES = [
  'component',
  'directive',
  'filter'
]

Vue is so in fact 初始化了 3 个全局函数, and if it typeis component, and definitionis an object, then through this.opitons._base.extend, equivalent to Vue.extendconverting the object into a constructor inherits Vue, and finally through this.options[type + 's'][id] = definitionthe mount it Vue.options.componentson.
Insert picture description here

Because each of us is created by the components are Vue.extendinherited, we have analyzed before such a period of logic in the succession process:

Sub.options = mergeOptions(
  Super.options,
  extendOptions
)

Insert picture description here

That it will Vue.optionsmerge into Sub.options, which is the component optionson the stage and in the instance of the component, performs merge optionslogic, to Sub.options.componentsmerge into vm.$options.componentsthe.

Then in the creation vnodeprocess, performs _createElementthe method, let's look at the logic of this part, which is defined in src/core/vdom/create-element.jsthe:

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)
    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)
    } else {
    
    
      // unknown or unlisted namespaced elements
      // check at runtime because it may get assigned a namespace when its
      // parent normalizes children
      vnode = new VNode(
        tag, data, children,
        undefined, undefined, context
      )
    }
  } else {
    
    
    // direct component options / constructor
    vnode = createComponent(tag, data, context, children)
  }
  // ...
}

Insert picture description here

There is a logical judgment isDef(Ctor = resolveAsset(context.$options, 'components', tag)), first look at resolveAssetthe definition, src/core/utils/options.jsin which:

/**
 * Resolve an asset.
 * This function is used because child instances need access
 * to assets defined in its ancestor chain.
 */
export function resolveAsset (
  options: Object,
  type: string,
  id: string,
  warnMissing?: boolean
): any {
    
    
  /* istanbul ignore if */
  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]
  if (process.env.NODE_ENV !== 'production' && warnMissing && !res) {
    
    
    warn(
      'Failed to resolve ' + type.slice(0, -1) + ': ' + id,
      options
    )
  }
  return res
}

This logic is very simple, first pass const assets = options[type]got assets, and then try to get assets[id], there is a sequence to be used directly idtake, if not, put idbecomes a 驼峰form of shouting, if it still does not exist 在驼峰的基础上把首字母再变成大写in the form of shouting, if Report an error if you still can't get it. This shows that we use Vue.component(id, definition)when global registration component, idcan be 连字符, 驼峰or 首字母大写form.
Insert picture description here

So back to our calling resolveAsset(context.$options, 'components', tag), namely to take vm.$options.components[tag], so that we can in resolveAssettime to get the constructor of this component, and as a createComponentparameter of the hook.
Insert picture description here

Partial registration

Vue.js also support local registration, we can use within a component componentsoptions do local registration components, such as:

import HelloWorld from './components/HelloWorld'

export default {
    
    
  components: {
    
    
    HelloWorld
  }
}

Actually understand the process of global registration, local registration is very simple. There is a phase of consolidation in the instance of a component of the Vue option's logic, we have analyzed before, so put the componentsmerger into vm.$options.componentsthe, so that we can resolveAssetget this time 组件的构造函数, and as a createComponenthook parameter.

Note that the local and global registration Registration is different, 只有该类型的组件才可以访问局部注册的子组parts, and global registration is extended to the Vue.optionsnext, so all components created in the process, will be from the global Vue.options.componentsexpansion of the current components vm.$options.components, the components of which is the global registration can be arbitrary Reason for use.

to sum up

Through the analysis of this section, we 组件的注册过程have knowledge and understanding of 全局注册and 局部注册differences. In fact, in our daily work, when we use the component library, the more general basic components are often registered globally, while the business components of the prepared special scenarios are registered locally. Knowing their principles has a very good guiding significance for whether we use global registration components or partial registration components in our work.

PS: The
next section uses less asynchronous components, skip it for now, and make up later, and go directly to the next 深入响应式原理chapter.

Vue source code learning directory

Vue source code learning-componentization (1) createComponent
Vue source code learning-componentization (2) patch
Vue source code learning-componentization (3) merge configuration
Vue source code learning-componentization (4) life cycle

Click to go back to the complete list of Vue source code learning


谢谢你阅读到了最后~
期待你关注、收藏、评论、点赞~
让我们一起 变得更强

Guess you like

Origin blog.csdn.net/weixin_42752574/article/details/113702803