vue: mixins混入和extends继承

3,694 阅读3分钟

minxins

1. 作用

  • 类比directives的作用是减少DOM的重复操作。
  • mixins的作用是减少data、methods、钩子的重复使用。

2. 举例一个场景

假设我们需要在每个组件上添加name和time。在created时打出提示,并在beforeDestoryed报出存活时间。一共有5个组件,应该怎么做?

  • 方法一: 给每个组件添加data和钩子,共需要5次
  • 方法二: 使用mixins减少重复。

来看代码: 假设使用方法一:

<template>
  <div>Child1</div>
</template>

<script>
export default {
  data() {
    return {
      name: "Child1",
      time: undefined,
      now:undefined,
    };
  },
  beforeDestroy() {
   
    this.now = new Date()
    console.log(`${this.name}死亡了,共生存了 ${this.now - this.time} ms`);
  },
  
  created() {
   this.time = new Date()
    console.log("Child 1 的 created");
  },
 
};
</script>

五个组件需要重复五次。下面使用mixins

新建log.js。 把重复的部分提出来。

const log = {
  data() {
    return {
      name: undefined,
      time: undefined
    };
  },
  created() {
    if (!this.name) {
      throw new Error("need name");
    }
    this.time = new Date();
    console.log(`${this.name}出生了`);
  },
  beforeDestroy() {
    const now = new Date();

    console.log(`${this.name}死亡了,共生存了 ${now - this.time} ms`);
  }
};

export default log;

在需要应用的组件里

<template>
  <div>Child2</div>
</template>

<script>
// 引入mixins
import log from "../mixins/log.js";
export default {
  data() {
    return {
      name: "Child2"
    };
  },
  mixins: [log]
};
</script>

codesandbox.io/s/stoic-rgb…

虽然此处,两个组件用可以通过this.name引用mixins中定义的name,但是两个组件引用的并不是同一个,而是各自创建了一个新的。如果在组件中定义相同的data,则此处会引用组件中的name,而非mixins中的

方法的覆盖

如果在引用mixins的同时,在组件中重复定义相同的方法,则mixins中的方法会被覆盖。

3. 选项合并

  1. 当组件和混入对象含有同名选项时,这些选项将以恰当的方式进行“合并”。比如,数据对象在内部会进行递归合并,并在发生冲突时以组件数据优先
var mixin = {
  data: function () {
    return {
      message: 'hello',
      foo: 'abc'
    }
  }
}

new Vue({
  mixins: [mixin],
  data: function () {
    return {
      message: 'goodbye',
      bar: 'def'
    }
  },
  created: function () {
    console.log(this.$data)
    // => { message: "goodbye", foo: "abc", bar: "def" }
  }
})
  1. 同名钩子函数将合并为一个数组,因此都将被调用。另外,混入对象的钩子将在组件自身钩子之前调用。
var mixin = {
  created: function () {
    console.log('混入对象的钩子被调用')
  }
}

new Vue({
  mixins: [mixin],
  created: function () {
    console.log('组件钩子被调用')
  }
})

// => "混入对象的钩子被调用"
// => "组件钩子被调用"
  1. 值为对象的选项,例如 methods、components 和 directives,将被合并为同一个对象。两个对象键名冲突时,取组件对象的键值对
var mixin = {
  methods: {
    foo: function () {
      console.log('foo')
    },
    conflicting: function () {
      console.log('from mixin')
    }
  }
}

var vm = new Vue({
  mixins: [mixin],
  methods: {
    bar: function () {
      console.log('bar')
    },
    conflicting: function () {
      console.log('from self')
    }
  }
})

vm.foo() // => "foo"
vm.bar() // => "bar"
vm.conflicting() // => "from self"

4. 全局混入

混入也可以进行全局注册。使用时格外小心!一旦使用全局混入,它将影响每一个之后创建的 Vue 实例。使用恰当时,这可以用来为自定义选项注入处理逻辑。

// 为自定义的选项 'myOption' 注入一个处理器。
Vue.mixin({
  created: function () {
    var myOption = this.$options.myOption
    if (myOption) {
      console.log(myOption)
    }
  }
})

new Vue({
  myOption: 'hello!'
})
// => "hello!"

extends继承

  • vue的extends和mixins类似,通过暴露一个extends对象到组件中使用。

  • extends会比mixins先执行。执行顺序:extends > mixins > 组件

  • extends只能暴露一个extends对象,暴露多个extends不会执行。

//暴露两个mixins对象
export const mixinsTest = {
    methods: {
        hello() {
            console.log("hello mixins");
        }
    },
    beforeCreate(){
        console.log("混入的beforeCreated");
        
    },
    created() {
        this.hello();
    },
}


export const mixinsTest2 = {
    methods:{
        hello2(){
            console.log("hello2");
        }
    },
    created() {
        this.hello2();
    },
}
//只能使用一个extends对象,多个无效,extends会先于mixins执行
export const extendsTest = {
    methods: {
        hello3() {
            console.log("hello extends");
        }
    },
    beforeCreate(){
        console.log("extends的beforeCreated");
        
    },
    created() {
        this.hello3();
    },
}

在组件中使用

<template>
<div>
    home
</div>
</template>
<script>
import {mixinsTest,mixinsTest2,extendsTest} from '../util/test.js'
export default {
  name: "Home",
  data () {
    return {
    };
  },
    beforeCreate(){
        console.log("组件的beforeCreated");
        
    },
  created(){
      console.log("1212");
  },
  mixins:[mixinsTest2,mixinsTest], // 先调用那个mixins对象,就先执行哪个
 extends:extendsTest  // 使用extends
}
</script>
<style lang="css" scoped>
</style>

总结

对比mixins和extends

  1. extends传入的是对象写法,而mixins是数组写法
  2. mixins可以混入多个mixin,extends只能继承一个。
  3. 优先级Vue.extend>extends>mixins
  4. 调用方式:
 mixins: [mixin1, mixin2]
 extends: extendsTest

  1. 都是对父组件的扩充,包括methods、components、钩子等.
  2. 触发钩子函数时,先调用extends,mixins的函数,再调用父组件的函数。
  3. 虽然也能在创建mixin时添加data、template属性,但当父组件也拥有此属性时以父为准。
  4. data、methods内函数、components和directives等键值对格式的对象均以父组件/实例为准

继承钩子

const extend = {
  created () {
    console.log('extends created')
  }
}
const mixin1 = {
  created () {
    console.log('mixin1 created')
  }
}
const mixin2 = {
  created () {
    console.log('mixin2 created')
  }
}
export default {
  extends: extend,
  mixins: [mixin1, mixin2],
  name: 'app',
  created () {
    console.log('created')
  }
}

输出

extends created
mixin1 created
mixin2 created
created
  • 结论: 优先调用mixins和extends继承的父类,extends触发的优先级更高,相对于是队列
  • push(extend, mixin1, minxin2, 本身的钩子函数)
  • 经过测试,watch的值继承规则一样。

继承methods

const extend = {
  data () {
    return {
      name: 'extend name'
    }
  }
}
const mixin1 = {
  data () {
    return {
      name: 'mixin1 name'
    }
  }
}
const mixin2 = {
  data () {
    return {
      name: 'mixin2 name'
    }
  }
}
// name = 'name'
export default {
  mixins: [mixin1, mixin2],
  extends: extend,
  name: 'app',
  data () {
    return {
      name: 'name'
    }
  }
}
// 只写出子类,name = 'mixin2 name',extends优先级高会被mixins覆盖
export default {
  mixins: [mixin1, mixin2],
  extends: extend,
  name: 'app'
}
// 只写出子类,name = 'mixin1 name',mixins后面继承会覆盖前面的
export default {
  mixins: [mixin2, mixin1],
  extends: extend,
  name: 'app'
}
  • 结论:子类再次声明,data中的变量都会被重写,以子类的为准。
  • 如果子类不声明,data中的变量将会最后继承的父类为准。
  • 经过测试,props中属性、methods中的方法和computed的值继承规则一样