【Vue源码】第十四节组件化之简单了解组件注册和异步组件

组件注册

在 Vue.js 中,除了它内置的组件如 keep-alivecomponenttransitiontransition-group 等,其它用户自定义组件在使用前必须注册。

全局注册

要注册一个全局组件,可以使用 Vue.component(tagName, options)。例如:

Vue.component('my-component', {
    
    
  // 选项
})

Vue.component 函数的定义过程发生在最开始初始化 Vue 的全局函数的时候,代码在 src/core/global-api/assets.js 中:

import {
    
     ASSET_TYPES } from 'shared/constants'
import {
    
     isPlainObject, validateComponentName } from '../util/index'
export const ASSET_TYPES = [
  'component',
  'directive',
  'filter'
]
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
      }
    }
  })
}

局部注册

局部注册会把组件扩展到 Sub.options 下:

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

总结

  • 全局注册的组件可以在任意地方使用,因为组件会通过 Vue.component 函数扩展到 Vue.options上,而各个组件初始化时都会将 Vue.options 与自身 options 合并,这样每个组件都能访问到这个全局注册的组件。
  • 局部注册的组件只能在当前组件使用,因为组件仅仅只是扩展到 Sub.options 也就是当前组件构造函数的 options 上。

异步组件

在官网中异步组件有三种写法:

Vue.component('async-example', function (resolve, reject) {
    
    
  setTimeout(function () {
    
    
    // 向 `resolve` 回调传递组件定义
    resolve({
    
    
      template: '<div>I am async!</div>'
    })
  }, 1000)
})
Vue.component(
  'async-webpack-example',
  // 这个 `import` 函数会返回一个 `Promise` 对象。
  () => import('./my-async-component')
)
const AsyncComponent = () => ({
    
    
  // 需要加载的组件 (应该是一个 `Promise` 对象)
  component: import('./MyComponent.vue'),
  // 异步组件加载时使用的组件
  loading: LoadingComponent,
  // 加载失败时使用的组件
  error: ErrorComponent,
  // 展示加载时组件的延时时间。默认值是 200 (毫秒)
  delay: 200,
  // 如果提供了超时时间且组件加载也超时了,
  // 则使用加载失败时使用的组件。默认值是:`Infinity`
  timeout: 3000
})

分析一下createComponent函数中的resolveAsyncComponent

export function createComponent (
  Ctor: Class<Component> | Function | Object | void,
  data: ?VNodeData,
  context: Component,
  children: ?Array<VNode>,
  tag?: string
): VNode | Array<VNode> | void {
    
    
  if (isUndef(Ctor)) {
    
    
    return
  }

  const baseCtor = context.$options._base

  // 由于组件的定义并不是一个普通对象,所以不会执行 Vue.extend 的逻辑把它变成一个组件的构造函数
  if (isObject(Ctor)) {
    
    
    Ctor = baseCtor.extend(Ctor)
  }
  
  // ...

  // 异步组件
  let asyncFactory
  if (isUndef(Ctor.cid)) {
    
    
    asyncFactory = Ctor
    // 处理了 3 种异步组件的创建方式
    Ctor = resolveAsyncComponent(asyncFactory, baseCtor, context)
    if (Ctor === undefined) {
    
    
      // 实际上就是就是创建了一个占位的注释 VNode,同时把 asyncFactory 和 asyncMeta 赋值给当前 vnode。
      return createAsyncPlaceholder(
        asyncFactory,
        data,
        context,
        children,
        tag
      )
    }
  }
}

猜你喜欢

转载自blog.csdn.net/qq_34086980/article/details/105934100