Skip to content

0x035-Vue源码解读-Vue-component


接上文 [0x030-Vue2-6源码阅读1-项目结构和初始化],观察一下 Vue.component 的初始化:

Vue.component的使用方法

Vue.component() 是 Vue.js 中用于全局注册组件的一个方法。通过使用它,我们可以在 Vue 应用程序的任何地方使用该组件。

使用示例:

  • 在 Vue 根实例中注册组件:
javascript
Vue.component('button-counter', {
  data: function () {
    return {
      count: 0
    }
  },
  template: '<button @click="count++">
  You clicked me {{ count }} times.
  </button>'
})
Vue.component('button-counter', {
  data: function () {
    return {
      count: 0
    }
  },
  template: '<button @click="count++">
  You clicked me {{ count }} times.
  </button>'
})
  • 在组件中使用这个全局注册的组件:
html
<template>
  <div id="app">
    <button-counter></button-counter>
  </div>
</template>
<template>
  <div id="app">
    <button-counter></button-counter>
  </div>
</template>

初始化

src/core/global-api/assets.js

ts
export const ASSET_TYPES = [  
  'component',  
  'directive',  
  'filter'  
]
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 {          
        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  
      }  
    }  
  })  
}
export const ASSET_TYPES = [  
  'component',  
  'directive',  
  'filter'  
]
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 {          
        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  
      }  
    }  
  })  
}

简化下component的代码:

js
Vue.component = function (  
      id: string,  
      definition: Function | Object  
    ): Function | Object | void {  
      if (!definition) { 
		 // 没有definition的时候,返回定义的组件     
        return this.options[type + 's'][id]  
      } else {
	    // 如果没有名字, definition.name = id
        definition.name = definition.name || id;
        // 调用Vue.extend扩展definition(option)
        definition = this.options._base.extend(definition);
        this.options.components[id] = definition  
        return definition  
      }  
    }  
  }
Vue.component = function (  
      id: string,  
      definition: Function | Object  
    ): Function | Object | void {  
      if (!definition) { 
		 // 没有definition的时候,返回定义的组件     
        return this.options[type + 's'][id]  
      } else {
	    // 如果没有名字, definition.name = id
        definition.name = definition.name || id;
        // 调用Vue.extend扩展definition(option)
        definition = this.options._base.extend(definition);
        this.options.components[id] = definition  
        return definition  
      }  
    }  
  }
js
export function initExtend (Vue: GlobalAPI) {
  /**
   * 每个实例构造函数(包括 Vue)都有一个唯一的 cid。这使我们能够创建包装的“子构造函数”以进行原型继承并缓存它们。
   */
  Vue.cid = 0
  let cid = 1

  /**
   * Class inheritance
   */
  Vue.extend = function (extendOptions: Object): Function {
    extendOptions = extendOptions || {}
    const Super = this
    // 第一次extend 进入 cid = 0
    const SuperId = Super.cid
    // 第一次extend 进入 cachedCtors = {}
    // `_Ctor`在Vue源码中只有本function用到了。子组件options._Ctor缓存父组件的id(唯一),防止重复调用的时候重复调用后续代码
    const cachedCtors = extendOptions._Ctor || (extendOptions._Ctor = {})
    if (cachedCtors[SuperId]) {
      return cachedCtors[SuperId]
    }

    const name = extendOptions.name || Super.options.name

    const Sub = function VueComponent (options) {
      // 后面 new VueExtend(...)的时候调用
      this._init(options)
    }
    Sub.prototype = Object.create(Super.prototype)
    Sub.prototype.constructor = Sub
    Sub.cid = cid++
    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

    // 将component等等函数也挂载到子组件
    ASSET_TYPES.forEach(function (type) {
      Sub[type] = Super[type]
    })
    // enable recursive self-lookup
    if (name) {
      Sub.options.components[name] = Sub
    }

    // keep a reference to the super options at extension time.
    // later at instantiation we can check if Super's options have
    // been updated.
    Sub.superOptions = Super.options
    Sub.extendOptions = extendOptions
    Sub.sealedOptions = extend({}, Sub.options)

    // cache constructor
    cachedCtors[SuperId] = Sub
    return Sub
  }
}
export function initExtend (Vue: GlobalAPI) {
  /**
   * 每个实例构造函数(包括 Vue)都有一个唯一的 cid。这使我们能够创建包装的“子构造函数”以进行原型继承并缓存它们。
   */
  Vue.cid = 0
  let cid = 1

  /**
   * Class inheritance
   */
  Vue.extend = function (extendOptions: Object): Function {
    extendOptions = extendOptions || {}
    const Super = this
    // 第一次extend 进入 cid = 0
    const SuperId = Super.cid
    // 第一次extend 进入 cachedCtors = {}
    // `_Ctor`在Vue源码中只有本function用到了。子组件options._Ctor缓存父组件的id(唯一),防止重复调用的时候重复调用后续代码
    const cachedCtors = extendOptions._Ctor || (extendOptions._Ctor = {})
    if (cachedCtors[SuperId]) {
      return cachedCtors[SuperId]
    }

    const name = extendOptions.name || Super.options.name

    const Sub = function VueComponent (options) {
      // 后面 new VueExtend(...)的时候调用
      this._init(options)
    }
    Sub.prototype = Object.create(Super.prototype)
    Sub.prototype.constructor = Sub
    Sub.cid = cid++
    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

    // 将component等等函数也挂载到子组件
    ASSET_TYPES.forEach(function (type) {
      Sub[type] = Super[type]
    })
    // enable recursive self-lookup
    if (name) {
      Sub.options.components[name] = Sub
    }

    // keep a reference to the super options at extension time.
    // later at instantiation we can check if Super's options have
    // been updated.
    Sub.superOptions = Super.options
    Sub.extendOptions = extendOptions
    Sub.sealedOptions = extend({}, Sub.options)

    // cache constructor
    cachedCtors[SuperId] = Sub
    return Sub
  }
}

由定义过程(this._init(options))可以知道调用this._init(options) 的不仅有父组件还是子组件

组件初始化代码

代码位置:

js
export function createComponentInstanceForVnode (
  // we know it's MountedComponentVNode but flow doesn't
  vnode: any,
  // activeInstance in lifecycle state
  parent: any
): Component {
  const options: InternalComponentOptions = {
    _isComponent: true,
    // 当前组件的VNode,对要创建的子组件来说是父Node
    _parentVnode: vnode, 
    parent
  }
  // check inline-template render functions
  const inlineTemplate = vnode.data.inlineTemplate
  if (isDef(inlineTemplate)) {
    options.render = inlineTemplate.render
    options.staticRenderFns = inlineTemplate.staticRenderFns
  }
  return new vnode.componentOptions.Ctor(options)
}
export function createComponentInstanceForVnode (
  // we know it's MountedComponentVNode but flow doesn't
  vnode: any,
  // activeInstance in lifecycle state
  parent: any
): Component {
  const options: InternalComponentOptions = {
    _isComponent: true,
    // 当前组件的VNode,对要创建的子组件来说是父Node
    _parentVnode: vnode, 
    parent
  }
  // check inline-template render functions
  const inlineTemplate = vnode.data.inlineTemplate
  if (isDef(inlineTemplate)) {
    options.render = inlineTemplate.render
    options.staticRenderFns = inlineTemplate.staticRenderFns
  }
  return new vnode.componentOptions.Ctor(options)
}