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>
虽然此处,两个组件用可以通过this.name引用mixins中定义的name,但是两个组件引用的并不是同一个,而是各自创建了一个新的。如果在组件中定义相同的data,则此处会引用组件中的name,而非mixins中的
方法的覆盖
如果在引用mixins的同时,在组件中重复定义相同的方法,则mixins中的方法会被覆盖。
3. 选项合并
- 当组件和混入对象含有同名选项时,这些选项将以恰当的方式进行“合并”。比如,数据对象在内部会进行递归合并,并在发生冲突时以组件数据优先。
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" }
}
})
- 同名钩子函数将合并为一个数组,因此都将被调用。另外,混入对象的钩子将在组件自身钩子之前调用。
var mixin = {
created: function () {
console.log('混入对象的钩子被调用')
}
}
new Vue({
mixins: [mixin],
created: function () {
console.log('组件钩子被调用')
}
})
// => "混入对象的钩子被调用"
// => "组件钩子被调用"
- 值为对象的选项,例如 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
- extends传入的是对象写法,而mixins是数组写法
- mixins可以混入多个mixin,extends只能继承一个。
- 优先级Vue.extend>extends>mixins
- 调用方式:
mixins: [mixin1, mixin2]
extends: extendsTest
- 都是对父组件的扩充,包括methods、components、钩子等.
- 触发钩子函数时,先调用extends,mixins的函数,再调用父组件的函数。
- 虽然也能在创建mixin时添加data、template属性,但当父组件也拥有此属性时以父为准。
- 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的值继承规则一样