Vue.component和Vue.extend内部的实现

1,044 阅读1分钟
Vue.component = function(id, definition) {
  if (!definition) {
    return this.options.components[id]
  } else {
    if (isPlainObject(definition)) {
      definition.name = definition.name || id
      definition = this.options._base.extend(definition)
    }
    this.options.components[id] = definition
    return definition
  }
}

简单解读一下就是:

如果 definition 不存在,说明此前被全局注册过,那就去 Vue.options.components 中找到对应的组件返回;

如果 definition 存在,说明这是一个新的全局组件,需要被全局注册。先判断 definition 是否是一个对象,如果是则将 definition 创建为继承自 Vue构造函数 的子类实例,如果不是则说明是一个已经继承自子类的实例,将其放置到 Vue.options.components 中,然后返回。

有一些绕,那我们就通过代码来进行说明:

// 方式1
const childComponent = Vue.extend({
  data: {},
  created() {},
  methods: {},
})
Vue.component('child', childComponent)

// 方式2
Vue.component('child', {
  name: 'child-component',
  data: {},
  created() {},
  methods: {},
})

如果是方式1,那么这时候不会进入 isPlainObject(definition) 的判断,直接将 childComponent 放置到 this.options.components['child'] 中,然后将 childComponent 返回。

如果是方式2,那么这时候会进入 isPlainObject(definition) 的判断,当前 definition.name 有值且为 'child-component',definition 会继续走入

this.options._base.extend(definition),这个方法其实就是 Vue.extend,然后将继承自 Vue构造函数 的子类的实例返回给 definition,再将这个 definition 放置到 this.options.components['child-component'] 中,最后将 definition 返回。

通过这两种方式我们可以知道,通过 Vue.component(id, definition) 注册的组件都会经过一层 Vue.extend(definition) 的包装生成一个子类实例,我们取名为 componentVM ,然后将这个 componentVM 放入 Vue.options.components 中,最后返回 componentVM。

从 Vue.component 可以得知,组件化的核心其实是在于 Vue.extend,通过 Vue.extend 包装过后的实例才是真正的组件

Vue.extend = function (extendOptions) {
  extendOptions = extendOptions || {}
  const Super = this
  const SuperId = Super.cid
  const cachedCtors = extendOptions._Ctor || (extendOptions._Ctor = {})
  if (cachedCtors[SuperId]) return cachedCtors[SuperId]
  const name = extendOptions.name || Super.options.name
  const Sub = function VueComponent (options) {
    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
  if (Sub.options.props) {
    initProps(Sub)
  }
  if (Sub.options.computed) {
    initComputed(Sub)
  }
  Sub.extend = Super.extend
  Sub.mixin = Super.mixin
  Sub.use = Super.use
  ASSET_TYPES.forEach(function (type) {
    Sub[type] = Super[type]
  })
  if (name) {
    Sub.options.components[name] = Sub
  }
  Sub.superOptions = Super.options
  Sub.extendOptions = extendOptions
  Sub.sealedOptions = extend({}, Sub.options)
  cachedCtors[SuperId] = Sub
  return Sub
}