Vue2.x source code learning (3)-componentization (2)

Merge configuration

Through source code analysis section before we know, new Vuea process usually scene 2, is an external active call our code new Vue(options)of a instantiate Vuean object; the other components of the process is that we analyzed on an inside by new Vue(options)instantiating Subassembly.

In either scenario, the instance will execute _init(options)the method, it will first perform a merge optionslogic-related code src/core/instance/init.jsin:

Vue.prototype._init = function (options?: Object) {
    
    
  // merge options
  if (options && options._isComponent) {
    
    
    // optimize internal component instantiation
    // since dynamic options merging is pretty slow, and none of the
    // internal component options needs special treatment.
    initInternalComponent(vm, options)
  } else {
    
    
    vm.$options = mergeOptions(
      resolveConstructorOptions(vm.constructor),
      options || {
    
    },
      vm
    )
  }
  // ...
}

We can see different scenarios for optionsconsolidation logic is not the same, and the incoming optionsvalues also have a very big difference, then I will introduce separate two kinds of scenes optionsmerge process.

To be more intuitive, we can give a simple example:

import Vue from 'vue'

let childComp = {
    
    
  template: '<div>{
    
    {msg}}</div>',
  created() {
    
    
    console.log('child created')
  },
  mounted() {
    
    
    console.log('child mounted')
  },
  data() {
    
    
    return {
    
    
      msg: 'Hello Vue'
    }
  }
}

Vue.mixin({
    
    
  created() {
    
    
    console.log('parent created')
  }
})

let app = new Vue({
    
    
  el: '#app',
  render: h => h(childComp)
})

External call scene

When the execution new Vuetime, the execution this._init(options)time will merge logic to perform the following options:

vm.$options = mergeOptions(
  resolveConstructorOptions(vm.constructor),
  options || {
    
    },
  vm
)

Here by calling mergeOptionsto merge method, which is actually the resolveConstructorOptions(vm.constructor)return value and optionsdo merge, resolveConstructorOptionsthe realization is not considered, in our scenario, it is simple returns vm.constructor.options, the equivalent Vue.options, then the value of what is it, in fact, in initGlobalAPI(Vue)the when defining this value, the code src/core/global-api/index.jsin:

export function initGlobalAPI (Vue: GlobalAPI) {
    
    
  // ...
  Vue.options = Object.create(null)
  ASSET_TYPES.forEach(type => {
    
    
    Vue.options[type + 's'] = Object.create(null)
  })

  // this is used to identify the "base" constructor to extend all plain-object
  // components with in Weex's multi-instance scenarios.
  Vue.options._base = Vue

  extend(Vue.options.components, builtInComponents)
  // ...
}

First, by Vue.options = Object.create(null)creating an empty object, and then traverse ASSET_TYPES, ASSET_TYPESthe definition src/shared/constants.jsof:

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

Therefore traversal above ASSET_TYPEScode corresponds to:

Vue.options.components = {
    
    }
Vue.options.directives = {
    
    }
Vue.options.filters = {
    
    }

Then it was executed Vue.options._base = Vue, and its function was introduced when we instantiated the sub-components in the previous section.

Finally extend(Vue.options.components, builtInComponents)extended to a number of built-in components Vue.options.componentson Vuethe built-in components are currently <keep-alive>, <transition>and <transition-group>components, which is why we use other components <keep-alive>reason components do not require registration, this child follow us <keep-alive>assembly time will tell in detail.

So back to mergeOptionsthis function, which is defined src/core/util/options.jsin:

/**
 * Merge two option objects into a new one.
 * Core utility used in both instantiation and inheritance.
 */
export function mergeOptions (
  parent: Object,
  child: Object,
  vm?: Component
): Object {
    
    
  if (process.env.NODE_ENV !== 'production') {
    
    
    checkComponents(child)
  }

  if (typeof child === 'function') {
    
    
    child = child.options
  }

  normalizeProps(child, vm)
  normalizeInject(child, vm)
  normalizeDirectives(child)
  const extendsFrom = child.extends
  if (extendsFrom) {
    
    
    parent = mergeOptions(parent, extendsFrom, vm)
  }
  if (child.mixins) {
    
    
    for (let i = 0, l = child.mixins.length; i < l; i++) {
    
    
      parent = mergeOptions(parent, child.mixins[i], vm)
    }
  }
  const options = {
    
    }
  let key
  for (key in parent) {
    
    
    mergeField(key)
  }
  for (key in child) {
    
    
    if (!hasOwn(parent, key)) {
    
    
      mergeField(key)
    }
  }
  function mergeField (key) {
    
    
    const strat = strats[key] || defaultStrat
    options[key] = strat(parent[key], child[key], vm, key)
  }
  return options
}

mergeOptionsThe main function is to parentand childthese two objects, according to some consolidation strategy, combined into a new object and return. More central steps, the first recursive extendsand mixinsincorporated into parent, and then traverse the parentcall mergeField, then traverse child, if keynot parenton their own property, then call mergeField.

Here is an interesting mergeFieldfunction, which is different keywith different consolidation strategy. For example, for the life cycle function, its merge strategy is like this:

function mergeHook (
  parentVal: ?Array<Function>,
  childVal: ?Function | ?Array<Function>
): ?Array<Function> {
    
    
  return childVal
    ? parentVal
      ? parentVal.concat(childVal)
      : Array.isArray(childVal)
        ? childVal
        : [childVal]
    : parentVal
}

LIFECYCLE_HOOKS.forEach(hook => {
    
    
  strats[hook] = mergeHook
})

Which LIFECYCLE_HOOKSis defined in src/shared/constants.jsthe:

export const LIFECYCLE_HOOKS = [
  'beforeCreate',
  'created',
  'beforeMount',
  'mounted',
  'beforeUpdate',
  'updated',
  'beforeDestroy',
  'destroyed',
  'activated',
  'deactivated',
  'errorCaptured'
]

This defines Vue.js all hook function name, so the hook function, their strategies are merging mergeHookfunction. Implementation of this function is also very interesting, with a multi-layer three yuan operator, if the logic is not present childVal, returns parentVal; otherwise, then determines whether there is parentVal, if there is childValadded to parentValreturn the new array, otherwise childValthe array. So back mergeOptionsfunctions, once parentand childare defined the same hook function, then they will merge two hook function into an array.

Guess you like

Origin blog.csdn.net/lb1135909273/article/details/110863007