VUE源码分析之mixin全局方法

官网有以下解释:

全局注册一个混入,影响注册之后所有创建的每个 Vue 实例。插件作者可以使用混入,向组件注入自定义的行为。不推荐在应用代码中使用

 

其实就相当于注册了一些全局数据和方法,在注册混入之后的Vue实例中都可以使用。我们还是通过一个例子看看:

​
​
<!DOCTYPE html>
<html>
 
<head>
 
    <meta charset="utf-8">
    <title>vue demo</title>
 
</head>
 
 
<body>
 
<script src="./vue.js"></script>
 
<div id="app">
    {{message}}
</div>

<div id="app2">
    {{message}}
</div>
 
<script>
    
    var mixinObj = {
        methods: {
            func1: function(){
                console.log("mixinObj func1 is called");
                this.message = this.message + " " + "Vue";
            },
            func2: function(){
                console.log("mixinObj func2 is called");
                this.message = this.message + " " + "China";
            }
        }
    };

    Vue.mixin(mixinObj);

    const app = new Vue({
        el:"#app",
        data:{
            message: "hello"
        },
        methods: {
             func1: function(){
                console.log("func1 is called");
                this.message = this.message + " " + "World";
            }
        }
    });
    const app2 = new Vue({
        el:"#app2",
        data:{
            message: "Hi"
        },
        methods: {
             func3: function(){
                console.log("func3 is called");
                this.message = this.message + " " + "World";
            }
        }
    });

    settimeout(function(){
        app.func1();
    }, 3000);

    settimeout(function(){
        app2.func1();
    }, 5000);
    
</script>
 
</body>
</html>
 
 
​

​

这个小例子运行后,首先刷新出两行字符Hello, Hi   过了3秒后变成Hello World , Hi  ,过了5秒后变成Hello World, Hi Vue。

从页面的效果看,我们定义的mixinObj中的methods里面的方法确实混入了Vue实例中,并且如果Vue中的方法名重合的话会用Vue实例中的方法,也就是说如果重名就不混入。我们看看mixin方法是如何实现的。

  function initMixin$1 (Vue) {
    Vue.mixin = function (mixin) {
      this.options = mergeOptions(this.options, mixin);
      return this
    };
  }

Vue.mixin方法很简单,调用了mergeOptions方法,看这个方法之前我们先看看this.options 是什么。通过打印我们可以看到this.options里面的属性有components, filters, directives, _base。 这些属性是在initGlobal全局函数加入的:

    Vue.options = Object.create(null);
    ASSET_TYPES.forEach(function (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);
  var ASSET_TYPES = [
    'component',
    'directive',
    'filter'
  ];

一目了然。我们接着看看mergeOptions函数:

  function mergeOptions (
    parent,
    child,
    vm
  ) {
    {
      checkComponents(child);
    }

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

    normalizeProps(child, vm);
    normalizeInject(child, vm);
    normalizeDirectives(child);

    // Apply extends and mixins on the child options,
    // but only if it is a raw options object that isn't
    // the result of another mergeOptions call.
    // Only merged options has the _base property.
    if (!child._base) {
      if (child.extends) {
        parent = mergeOptions(parent, child.extends, vm);
      }
      if (child.mixins) {
        for (var i = 0, l = child.mixins.length; i < l; i++) {
          parent = mergeOptions(parent, child.mixins[i], vm);
        }
      }
    }

    var options = {};
    var key;
    for (key in parent) {
      //把parent属性合入到options里面
      mergeField(key);
    }
    for (key in child) {
      if (!hasOwn(parent, key)) {
       //如果parent属性中没有child的属性就把child的属性合入到options里面
        mergeField(key);
      }
    }
    function mergeField (key) {
      var strat = strats[key] || defaultStrat;
      options[key] = strat(parent[key], child[key], vm, key);
    }
    //最后返回parent 和 child合入的属性
    return options
  }

主要看看函数的后半部分,先定义一个options对象,对这个options进行操作最后return 这个options。这个过程最主要的是mergeField函数,strats定义了多个合并策略,我们看看methods合并策略:

  strats.props =
  strats.methods =
  strats.inject =
  strats.computed = function (
    parentVal,
    childVal,
    vm,
    key
  ) {
    if (childVal && "development" !== 'production') {
      assertObjectType(key, childVal, vm);
    }
    if (!parentVal) { return childVal } //如果父对象为空,直接返回子对象
    var ret = Object.create(null);
    extend(ret, parentVal); //把父对象拷贝到ret对象
    if (childVal) { extend(ret, childVal); } //如果子对象存在,就把子对象拷贝到ret对象,如果子
                                             //对象属性和ret中的对象属性相同,就覆盖
    return ret
  };

对methods策略进行了简单的注释。合并策略还是比较简单的。

接下来继续看我们这个小例子的mixin流程。我们知道mergeOptions方法的第一个参数this.options对象里面有components, filters, directives, _base 四个属性,第二个参数就是我们要合并的mixinObj,mixinOjb对象中只有我们定义的methods属性。所以调用Vue.mixin后在this.options对象上合入了methods属性。

在创建Vue实例时也会进行合并,我们看下:

    Vue.prototype._init = function (options) {
      var vm = this;
      // a uid
      vm._uid = uid$3++;

      var startTag, endTag;
      /* istanbul ignore if */
      if (config.performance && mark) {
        startTag = "vue-perf-start:" + (vm._uid);
        endTag = "vue-perf-end:" + (vm._uid);
        mark(startTag);
      }

      // a flag to avoid this being observed
      vm._isVue = true;
      // 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
        );
      }
      /* istanbul ignore else */
      {
        initProxy(vm);
      }
      // expose real self
      vm._self = vm;
      initLifecycle(vm);
      initEvents(vm);
      initRender(vm);
      callHook(vm, 'beforeCreate');
      initInjections(vm); // resolve injections before data/props
      initState(vm);
      initProvide(vm); // resolve provide after data/props
      callHook(vm, 'created');

      /* istanbul ignore if */
      if (config.performance && mark) {
        vm._name = formatComponentName(vm, false);
        mark(endTag);
        measure(("vue " + (vm._name) + " init"), startTag, endTag);
      }

      if (vm.$options.el) {
        vm.$mount(vm.$options.el);
      }
    };

在创建Vue实例初始化时也会调用mergeOptions 在进行到:

    for (key in parent) {
      mergeField(key);
    }
   
    function mergeField (key) {
      var strat = strats[key] || defaultStrat;
      options[key] = strat(parent[key], child[key], vm, key); //这时,child[key] 不为空
                  //把传递给Vue实例的optoins中的methods  func1方法覆盖了mixinObj中的methods 
                  //func1方法
    }

 在合并父对象options时把Vue实例中的methods的func1属性 覆盖了mixinObj methods的func1属性。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值