Interviewer: Tell me about your understanding of Vue's mixin. What are the application scenarios?

Interviewer: Tell me about your understanding of Vue's mixin. What are the application scenarios?

1. What is a mixin

MixinA class in an object-oriented programming language that provides method implementations. Other classes can access mixinthe methods of the class without having to be a subclass of it

MixinClasses are usually used as functional modules, and "mixed in" when the function is needed, which is conducive to code reuse and avoids the complexity of multiple inheritance

Mixins in Vue

First look at the official definition

mixin(mixins), which provide a very flexible way to distribute Vuereusable functionality in components.

The essence is actually an jsobject, which can contain any function options in our components, such as data, components, methods , created, computedetc.

We only need to pass the common function mixinsinto the options as an object, and when the component uses mixinsthe object, all mixinsthe options of the object will be mixed into the options of the component itself.

In Vuewe can mix in locally and globally

Partial mix-in

Define an mixinobject with optionscomponents dataand methodsproperties

var myMixin = {
    
    
  created: function () {
    
    
    this.hello()
  },
  methods: {
    
    
    hello: function () {
    
    
      console.log('hello from mixin!')
    }
  }
}

mixinsComponents call mixinobjects through properties

Vue.component('componentA',{
    
    
  mixins: [myMixin]
})

When this component is in use, it mixes mixinthe methods inside, automatically executes createdthe life hook, and executes hellothe method

global mixins

By Vue.mixin()making a global mixin

Vue.mixin({
    
    
  created: function () {
    
    
      console.log("全局混入")
    }
})

Special care is required when using global mixins, as it affects every component instance (including third-party components)

PS: Global mix-ins are often used for plug-in writing

Precautions:

When the component has mixinthe same options as the object, the option of the component will be overwritten when the recursive merge is mixinperformed

But if the same option is a life cycle hook, it will be merged into an array, the hook will be executed first mixin, and then the hook of the component will be executed

2. Usage scenarios

In daily development, we often encounter the same or similar code that needs to be used in different components, and the functions of these codes are relatively independent

VueAt this time, mixinthe same or similar code can be proposed through the function

for example

Define a modalpop-up window component, which is used isShowingto control the display internally

const Modal = {
    
    
  template: '#modal',
  data() {
    
    
    return {
    
    
      isShowing: false
    }
  },
  methods: {
    
    
    toggleShow() {
    
    
      this.isShowing = !this.isShowing;
    }
  }
}

Define a tooltipprompt box, which is internally isShowingcontrolled by

const Tooltip = {
    
    
  template: '#tooltip',
  data() {
    
    
    return {
    
    
      isShowing: false
    }
  },
  methods: {
    
    
    toggleShow() {
    
    
      this.isShowing = !this.isShowing;
    }
  }
}

By observing the above two components, it is found that the logic of the two is the same, and the code control display is also the same, which mixinwill come in handy at this time

First extract the common code, write amixin

const toggle = {
    
    
  data() {
    
    
    return {
    
    
      isShowing: false
    }
  },
  methods: {
    
    
    toggleShow() {
    
    
      this.isShowing = !this.isShowing;
    }
  }
}

In use of the two components, only need to introducemixin

const Modal = {
    
    
  template: '#modal',
  mixins: [toggle]
};
 
const Tooltip = {
    
    
  template: '#tooltip',
  mixins: [toggle]
}

Through the above small example, let us know that Mixinit is so interesting, convenient and practical to encapsulate some reusable functions

3. Source code analysis

start Vue.mixinwith

Source location: /src/core/global-api/mixin.js

export function initMixin (Vue: GlobalAPI) {
    
    
  Vue.mixin = function (mixin: Object) {
    
    
    this.options = mergeOptions(this.options, mixin)
    return this
  }
}

Mainly call merOptionsthe method

Source location: /src/core/util/options.js

export function mergeOptions (
  parent: Object,
  child: Object,
  vm?: Component
): Object {
    
    

if (child.mixins) {
    
     // 判断有没有mixin 也就是mixin里面挂mixin的情况 有的话递归进行合并
    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) // 先遍历parent的key 调对应的strats[XXX]方法进行合并
  }
  for (key in child) {
    
    
    if (!hasOwn(parent, key)) {
    
     // 如果parent已经处理过某个key 就不处理了
      mergeField(key) // 处理child中的key 也就parent中没有处理过的key
    }
  }
  function mergeField (key) {
    
    
    const strat = strats[key] || defaultStrat
    options[key] = strat(parent[key], child[key], vm, key) // 根据不同类型的options调用strats中不同的方法进行合并
  }
  return options
}

From the source code above, we get the following points:

  • Priority recursive processingmixins
  • First traverse the merge parent, keycall mergeFieldthe method to merge, and then save it in the variableoptions
  • Traversing again child, merging to make up what parentis not in key, call mergeFieldthe method to merge, and save it in the variableoptions
  • mergeFieldmerged by the function

The following are Vueseveral types of merge strategies for

  • Replacement
  • Combined
  • queue type
  • Overlay type

Replacement

The replacement type incorporates props, methods, inject,computed

strats.props =
strats.methods =
strats.inject =
strats.computed = function (
  parentVal: ?Object,
  childVal: ?Object,
  vm?: Component,
  key: string
): ?Object {
    
    
  if (!parentVal) return childVal // 如果parentVal没有值,直接返回childVal
  const ret = Object.create(null) // 创建一个第三方对象 ret
  extend(ret, parentVal) // extend方法实际是把parentVal的属性复制到ret中
  if (childVal) extend(ret, childVal) // 把childVal的属性复制到ret中
  return ret
}
strats.provide = mergeDataOrFn

The ones with the same name props, methods, inject, computedwill be replaced by the later ones

Combined

And the merge type has:data

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) // 执行data挂的函数得到对象
        var parentData = parentVal.call(vm, vm)        
        if (childData) {
    
                
            return mergeData(childData, parentData) // 将2个对象进行合并                                 
        } else {
    
                
            return parentData // 如果没有childData 直接返回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 (!to.hasOwnProperty(key)) {
    
    
            set(to, key, fromVal);
        }      
        // 存在相同属性,合并对象
        else if (typeof toVal =="object" && typeof fromVal =="object") {
    
    
            mergeData(toVal, fromVal);
        }
    }    
    return to
}

mergeDataThe function traverses all attributes of the data to be merged, and then merges according to different situations:

  • When the target data object does not contain the current attribute, call setthe method to merge (the set method is actually some method of merging and reassigning)
  • When the target data object contains the current attribute and the current value is a pure object, recursively merge the current object value, this is done to prevent the object from having new attributes

queue

Queued merges are: all lifetimes andwatch

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
})

// watch
strats.watch = function (
  parentVal,
  childVal,
  vm,
  key
) {
    
    
  // work around Firefox's Object.prototype.watch...
  if (parentVal === nativeWatch) {
    
     parentVal = undefined; }
  if (childVal === nativeWatch) {
    
     childVal = undefined; }
  /* istanbul ignore if */
  if (!childVal) {
    
     return Object.create(parentVal || null) }
  {
    
    
    assertObjectType(key, childVal, vm);
  }
  if (!parentVal) {
    
     return childVal }
  var ret = {
    
    };
  extend(ret, parentVal);
  for (var key$1 in childVal) {
    
    
    var parent = ret[key$1];
    var child = childVal[key$1];
    if (parent && !Array.isArray(parent)) {
    
    
      parent = [parent];
    }
    ret[key$1] = parent
      ? parent.concat(child)
      : Array.isArray(child) ? child : [child];
  }
  return ret
};

Life cycle hooks and watchare merged into an array, and then traversed in a positive order to execute

Overlay type

Overlapping merges are: component, directives,filters

strats.components=
strats.directives=

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

The superposition type is mainly through the layer-by-layer superposition of the prototype chain

summary:

  • Replacement strategies include props, methods, inject, computed, which are to replace the old parameters with new parameters with the same name
  • The merge strategy is to merge and reassign databy methodset
  • The queue-type strategy has life cycle functions and watchthe principle is to store the functions in an array, and then traverse them in order and execute them sequentially
  • Superposition types include component, directives, filters, and are superimposed layer by layer through the prototype chain

references

  • https://zhuanlan.zhihu.com/p/31018570
  • https://juejin.cn/post/6844904015495446536#heading-1
  • https://juejin.cn/post/6844903846775357453

Guess you like

Origin blog.csdn.net/weixin_52898349/article/details/132358801