preface

By exploring the source code for mixins today, we can see when mixins are combined and how they are combined for each type.

Look at mixins in Vue from source code

Let’s start with vue. mixin and see the source code

// vue/src/core/global-api/mixin.js
export function initMixin (Vue: GlobalAPI) {
  Vue.mixin = function (mixin: Object) {
    this.options = mergeOptions(this.options, mixin)
    return this}}Copy the code

When we use vue. mixin, we pass in an object, which is the parameter mixin in the source code, and then call mergeOptions to merge the global base options with the mixin

So what are the global base options?

// vue/src/core/global-api/index.js
Vue.options = Object.create(null)
ASSET_TYPES.forEach(type= > {
Vue.options[type + 's'] = Object.create(null)})Copy the code

Where ASSET_TYPES is [‘ Component ‘, ‘directive’, ‘filter’], which is the global base. Options are ‘Component ‘, ‘directive’, ‘filter’.

Let’s continue to look at how the mergeOptions method is merged (code truncated).

// vue/src/core/util/options.js
export function mergeOptions (parent: Object, child: Object, vm? : Component) :Object {

if (child.mixins) { // Check whether there are mixins, i.e., mixins in mixins, if there are, recursive merge
    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) // merge the parent key with strats[XXX]
  }
  for (key in child) {
    if(! hasOwn(parent, key)) {// If the parent has already handled a key, it does not
      mergeField(key) // Handle child keys that are not handled by parent keys}}function mergeField (key) {
    const strat = strats[key] || defaultStrat
    options[key] = strat(parent[key], child[key], vm, key) // Call different strats methods based on different types of options to merge
  }
  return options
}
Copy the code

The above code does these three things

  • Prioritizing recursive processing of mixins

  • Iterate over the key in the merged parent, call the mergeField method to merge, and save in the options variable

  • Iterate over the child and merge the keys that are not present in the parent. Call the mergeField method to merge the keys and save them in the options variable

In fact, the core of strats is the corresponding different types of processing methods, we will be divided into several types to see the corresponding merge strategy

Replace type

Props, methods, Inject, and computed are alternatives. Let’s look at the code

strats.props =
strats.methods =
strats.inject =
strats.computed = function (parentVal: ? Object, childVal: ? Object, vm? : Component, key: string): ?Object {
  if(! parentVal)return childVal // If parentVal has no value, return childVal
  const ret = Object.create(null) // Create a third-party object ret
  extend(ret, parentVal) The extend method actually copies the parentVal property into ret
  if (childVal) extend(ret, childVal) // Copy the attributes of childVal into RET
  return ret
}
strats.provide = mergeDataOrFn
Copy the code

As can be seen from the above code, the merge strategies of props, methods, Inject, and computed all replace old parameters with new parameters of the same name, which is called substitution type

Merge type

Data belongs to merge type, we will abbreviate the source code

strats.data = function(parentVal, childVal, vm) {    
    return mergeDataOrFn(
        parentVal, childVal, vm
    )
};

function mergeDataOrFn(parentVal, childVal, vm) {    
    return function mergedInstanceDataFn() {        
        var childData = childVal.call(vm, vm) // Execute the data function to get the object
        var parentData = parentVal.call(vm, vm)        
        if (childData) {            
            return mergeData(childData, parentData) // Merge two objects
        } else {            
            return parentData // If there is no childData, return parentData}}}function mergeData(to, from) {    
    if (!from) return to    
    var key, toVal, fromVal;    
    var keys = Object.keys(from);   
    for (var i = 0; i < keys.length; i++) {
        key = keys[i];
        toVal = to[key];
        fromVal = from[key];    
        // If this attribute does not exist, reset it
        if(! to.hasOwnProperty(key)) { set(to, key, fromVal); }// Merge objects if the same attributes exist
        else if (typeof toVal =="object" && typeof fromVal =="object") { mergeData(toVal, fromVal); }}return to
}
Copy the code

As you can see from the code, this iterates through all the attributes of the data to be merged, and then merges them accordingly:

  • When the target data object does not contain the current attributes, the merge is performed by calling the set method, which follows.
  • When the target data object contains the current attributes and the current values are pure objects, the current object values are recursively merged to prevent the object from having new attributes.

The set method is just a bunch of merge reassignment methods that we won’t expand here

Queue type

All lifecycle functions and Watches are queue merge strategies

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

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

The code is simple: the lifecycle hooks of the Vue instance are merged into an array and then executed in order. Watch’s strategy is similar, but won’t be expanded here

superposition

Component, directives, and filters are stacked policies, but are really rarely used in the business

strats.components=
strats.directives=

strats.filters = function mergeAssets(
    parentVal, childVal, vm, key
) {    
    var res = Object.create(parentVal || null);    
    if (childVal) { 
        for (var key inchildVal) { res[key] = childVal[key]; }}return res
}
Copy the code

Layer upon layer through a chain of prototypes

conclusion

It’s a little bit too much code, but if you still don’t understand it, it doesn’t matter, let’s just summarize it

  • When we call vue. mixin we merge the global base options(Component ‘, ‘directive’, ‘filter ‘) with the mergeOptions method

  • Recursive mixins merge first within mergeOptions, and then the parent and child call mergeField to merge. Different types of mixins merge differently

  • The alternative policies include props, methods, Inject, and computed, that is, replacing old parameters with new parameters of the same name

  • The merge type strategy is data, which is merged and reassigned using the set method

  • The queued strategy has lifecycle functions and Watches. The principle is to store the functions into an array and then execute them in order

  • The stacked types are Component, directives, and filters that link the callbacks together by a chain of principles