详解Vue的Mixin和合并策略

2,585 阅读1分钟

在Vue中,Mixin 提供了一种非常灵活的方式,来分发 Vue 组件中的可复用功能。一个 Mixin 对象可以包含任意组件选项。当组件使用 Mixin 对象时,所有 Mixin 对象的选项将被“混合”进入该组件本身的选项。

Mixin的特点

  • 可以复用逻辑,并且变量是独立的,在组件中是独立的不会相互影响
  • Mixin很容易造成命名冲突,因此在使用的时候必须要确保不会有冲突的属性名称,这样就会造成额外的起名负担~

Mixin的用法

全局用法

Vue.mixin({
    data() {
        return {
            myOption: "hello"
        };
    },
    created() {
        console.log(this.myOption, 'this.myOption');
    }
});

new Vue({
    router,
    store,
    render: h => h(App)
}).$mount('#app');

// 每一个子组件加载的时候都会打印出hello
// => "hello"

Mixin 也可以进行全局注册。使用时格外小心!一旦使用全局 mixin,它将影响每一个之后创建的组件 (例如,每个子组件)。

局部Mixin

const myMixin = {
  data() {
    return {
      myOption: 'hello'
    }
  }
}

export defualt {
    mixins:[myMixin],
    created(){
        console.log(this.myOption,'this.myOption');
    }
}

// 这样只有在子组件中mixin的才会被合并
// => "hello"

当组件和 mixin 对象含有同名选项时,这些选项将以恰当的方式进行 “合并”

Mixin的合并策略

在使用Mixin的时候不论是局部注册还是全局注册,在组件中如果有同名选项的时候,这些选项都会以一种方式进行合并,那么这种合并是如何而来的呢?又是如何定义的呢?这个时候就不得不看一下Vue源码中是如何定义Mixin的了。

Vue.mixin源码

// /src/core/global-api/mixin.js
import { mergeOptions } from '../util/index'

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

此时可以发现最主要的是用mergeOtpions方法对基础的optionsmixin进行了合并。 现在再来看mergeOptions方法是如何实现的?

// /src/core/util/options.js
export function mergeOptions (
  parent: Object,
  child: Object,
  vm?: Component
): Object {
  ...
    
  // 规范化属性  
  normalizeProps(child, vm)
  normalizeInject(child, vm)
  normalizeDirectives(child)

  // 此处注释掉了一些需要递归判断mixin中存在mixin的情况
  ...


  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
}

上面代码的主要含义: 1、递归处理mixin 2、遍历parent中的key,再调用mergeField进行合并,具体的合并策略在strats

// src/core/util/options.js(依然会省略此处不关心的代码)
import config from '../config'
const strats = config.optionMergeStrategies

// 合并data
strats.data = function (
  parentVal: any,
  childVal: any,
  vm?: Component
): ?Function {
    // 暂不考虑是如何合并的
    ...
}

// 合并watch
strats.watch = function (
  parentVal: ?Object,
  childVal: ?Object,
  vm?: Component,
  key: string
): ?Object {

// 暂不考虑是如何合并的
 ...
}

基础的options

  • 数据:data,props,propsData,computed,methods,watch
  • DOM:el,template,render,renderError
  • 生命周期钩子:
  • 资源: directives、filters、components
  • 组合: parent、extends、provide、inject

可以发现strats就是config.optionMrgeStrategies,用这个来实现了的Vue内部的data,watch,props等的一些合并策略,而config.optionMergeStrategies具体又是个什么呢?其实就是一个空对象而已,那么我们是否可以通过这个来自定义合并策略呢?答案是:当然可以

自定义合并策略

自定义选项在合并时,默认策略为简单地覆盖已有值。如果想让某个自定义选项以自定义逻辑进行合并,可以在 Vue.config.optionMergeStrategies 中添加一个函数:

Vue.config.optionMergeStrategies.customOption = (toVal, fromVal) => {
  // return mergedVal
}

合并策略接收在父实例和子实例上定义的该选项的值,分别作为第一个和第二个参数。

Vue.config.optionMergeStrategies.myMixin = function (toVal, fromVal) {
    console.log(toVal, fromVal);
    // => undefined "parent"
    // => parent reportList
    return fromVal || toVal;
};
const myMixin = {
    myMixin: 'parent'
};
export default {
    myMixin: 'reportList',
    mixins: [myMixin],
    created(){
        console.log(this.$options.myMixin);
        // => reportList
    }
}

虽然可以自定义合并策略,但是建议不要使用Vue中已有的options(例如data,props,watch,methods等)来合并,这样会冲突掉原来的逻辑,慎用的好!!!

总结

在 Vue 2 中,mixin 和合并策略是将部分组件逻辑抽象成可重用块的主要工具。但是,他们有几个问题:

  • mixin 很容易发生冲突:因为每个特性的属性都被合并到同一个组件中,所以为了避免 property 名冲突和调试,你仍然需要了解其他每个特性。

  • 可重用性是有限的:我们不能向 mixin 传递任何参数来改变它的逻辑,这降低了它们在抽象逻辑方面的灵活性。 为了解决这些问题,Vue3有了新方法:组合式 API,可以继续学习啦~

参考:

blog.csdn.net/qq_25324335… zhuanlan.zhihu.com/p/105023040