Writing in the front

As we all know, the soul of Vue is: data-driven view + componentization, this article will explain the creation of Vue components, and the related methods of vue.extend.

core/global-api/assets.js

The Vue.component method is defined in this file. I modified some of the source code to make it easier to understand, but the accuracy remains the same:

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
  }
}
Copy the code

If definition does not exist, it has been globally registered, so go to Vue.options.components and find the corresponding component. If definition exists, it is a new global component that needs to be registered globally. If so, create definition as an instance of a subclass inherited from the Vue constructor. If not, create definition as an instance of a subclass inherited from the Vue constructor. Place definition in Vue.options.components, and return. It’s a little convoluted, so let’s do it in code:

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

2 / / way
Vue.component('child', {
  name: 'child-component'.data: {},
  created() {},
  methods: {},})Copy the code

If this is method 1, then instead of going to isPlainObject(definition), put childComponent directly into this.options.components[‘child’]. Then return childComponent. If this is method 2, then the isPlainObject(definition) judgment is entered. Currently, definition. Name has a value and is ‘child-component’. Definition goes to this.options._base.extend(definition), which is essentially vue.extend, and returns an instance of a subclass that inherits from the Vue constructor to Definition, Put the definition in this.options.components[‘child-component’], and return the definition. Vue.component(id, definition) generates a componentVM instance by wrapping vue.extend (definition). Then put this componentVM into Vue.options.components, and finally return componentVM. If you don’t understand where this.options.components and this.options._base.extend come from, look at the creation of the Vue constructor -> core/global-api/index.js section in the previous chapter.

core/global-api/extend.js

Vue.com Ponent tells us that the core of componentization is vue.extend. The instances wrapped by vue.extend are the real components. What is the principle behind it? I modified the source code again, but the accuracy remains the same:

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
}
Copy the code

From the way vue.extend is written, it is clear that this is a static method of the Vue constructor, so the internal this refers to the Vue constructor itself, which is the Super defined in the function body. A simple interpretation of the source code is: Create a new VueComponent constructor named Sub that inherits all properties and methods from the Vue constructor prototype by pointing the VueComponent prototype to the Vue constructor prototype. Use/vue.mixin/vue.extend/Vue.component/vue.directive/vue.filter, so that VueComponent also has these static methods. Next, check whether the options passed in have props and computed properties and initialize them if they do. If the options passed in contain a name attribute, it is automatically placed in the global component of VueComponent (not to be confused with the global component of Vue). Then the Vue. Save the options to VueComponent. SuperOptions properties, will be introduced to save the options to VueComponent. ExtendOptions properties, And storing incoming options a save to VueComponent. SealedOptions. Finally, we return VueComponent, which is the constructor of the Vue component that inherits from the Vue constructor.

VueComponent = {
  cid,
  super: Vue,
  options: {
    _base: Vue,
    components: {
      KeepAlive,
      Transition,
      TransitionGroup,
    }
    directives: {
      'model'.'show',}filters: {},
    _Ctor: {},
    ...extendOptions
  },
  use() {},
  mixin() {},
  extend() {},
  component() {},
  directive() {},
  filter() {},
  superOptions,
  extendOptions,
  sealedOptions,
  prototype: {
    _init() {},
    $data: {},
    $props: {},
    $set() {},
    $del() {},
    $watch() {},
    $on() {},
    $once() {},
    $off() {},
    $emit() {},
    _update() {},
    $forceUpdate() {},
    $destroy() {},
    $nextTick() {},
    _render() {},
    _o() {},
    _n() {},
    _s() {},
    _l() {},
    _t() {},
    _q() {},
    _i() {},
    _m() {},
    _f() {},
    _k() {},
    _b() {},
    _v() {},
    _e() {},
    _u() {},
    _g() {},
    $isServer: false.$ssrContext: {},
    __patch__() {},
    $mount() {},
  }
}
Copy the code

_isComponent

If we are careful, we will notice that this._init(options) is called inside the VueComponent constructor. Yes, it was called when the Vue constructor was defined, in order to automatically enable initialization after new Vue(options). We don’t see new VueComponent(options) being used inside the VueComponent, except that we use new + $mount to mount a component directly with vue.extend. In vue.prototype. _init, the _isComponent property is not seen in vue.ponent or vue.extend. This is where the parent-child component lifecycle comes in. The new VueComponent(Options) is created for the new VueComponent(options), and the _isComponent is created for the new VueComponent(options). Parent component beforeCreate -> parent component created -> Parent component beforeMount -> child component beforeCreate -> child component created -> child component beforeMount -> Child component Mounted -> Parent component Mounted