Vue.js is an easy to use web app framework that we can use to develop interactive front end apps.
In this article, we’ll look at how to define and use mixins to make code reusable.
Basics
We can define a mixin by writing some simple code that consists of an object with some properties of components as follows:
src/index.js
:
const appMixin = {
created() {
this.hello();
},
methods: {
hello() {
this.message = "hi";
}
}
};
new Vue({
el: "#app",
mixins: [appMixin],
data: {
message: ""
}
});
index.html
:
<!DOCTYPE html>
<html>
<head>
<title>App</title>
<meta charset="UTF-8" />
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
{{message}}
</div>
<script src="src/index.js"></script>
</body>
</html>
In the code above, we defined the mixin appMixin
by assigning it to an object with the structure that are the same as the Vue instance.
Therefore, hello
gets called when the Vue instance is created since we have the created
hook in appMixin
.
Then in the template, we display the message
, so we get hi
on the screen.
Other properties we can put include methods
, components
, and directives
. They’ll all be merged into the object we pass into new Vue
or the component options object.
If there’s anything that’s the same, the component’s options will take priority over the items in the mixin.
For example, if we have the following:
src/index.js
:
const appMixin = {
created() {
this.hello();
},
methods: {
hello() {
this.message = "hi";
}
}
};
new Vue({
el: "#app",
mixins: [appMixin],
data: {
message: ""
},
created() {
this.hello();
},
methods: {
hello() {
this.message = "foo";
}
}
});
index.html
:
<!DOCTYPE html>
<html>
<head>
<title>App</title>
<meta charset="UTF-8" />
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
{{message}}
</div>
<script src="src/index.js"></script>
</body>
</html>
Then foo
is displayed since the methods in the Vue instance takes precedence over the methods in the mixin.
We can also use the Vue.extend
method to create a new component as follows:
src/index.js
:
const appMixin = {
created() {
this.hello();
},
methods: {
hello() {
this.message = "hi";
}
}
};
const Component = Vue.extend({
mixins: [appMixin],
el: "#app"
});
new Component();
index.html
:
<!DOCTYPE html>
<html>
<head>
<title>App</title>
<meta charset="UTF-8" />
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
{{message}}
</div>
<script src="src/index.js"></script>
</body>
</html>
Vue.extend
works the same way as new Vue
that we have above.
The same merging mechanism is used with Vue.extend()
.
Option Merging
We can adjust how items are merged by selecting the appropriate strategy for merging.
For example, data objects are merged recursively, with component’s data taking priority in case of conflicts.
src/index.js
:
const mixin = {
data() {
return {
message: "hi",
foo: "abc"
};
}
};
new Vue({
el: "#app",
mixins: [mixin],
data() {
return {
message: "bye",
bar: "def"
};
}
});
index.html
:
<!DOCTYPE html>
<html>
<head>
<title>App</title>
<meta charset="UTF-8" />
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
{{message}} {{foo}} {{bar}}
</div>
<script src="src/index.js"></script>
</body>
</html>
We see bye abc def
displayed since message
is in both the component and the mixin, and the component takes precedence.
foo
and bar
are only in the mixin and component respectively, so they’re both merged into the same data
object.
Hooks with the same name are merged into an array so that they all will be called. Mixin hooks will be called the component’s own hooks.
So if we have:
src/index.js
:
const mixin = {
mounted() {
console.log("mixin hook called");
}
};
new Vue({
el: "#app",
mixins: [mixin],
mounted() {
console.log("component hook called");
}
});
We see:
mixin hook called
component hook called
in the console.log
output.
Options that expect object values like methods
, components
, and directives
are merged into the same object. The component’s options will take priority when there’re conflicts in the objects.
For example, if we have:
src/index.js
:
const mixin = {
methods: {
foo() {
console.log("foo");
}
}
};
const vm = new Vue({
el: "#app",
mixins: [mixin],
methods: {
foo() {
console.log("bar");
}
}
});
vm.foo();
We’ll see bar
logged since the component’s foo
method takes precedence over the mixin
‘s foo
method.
The same merging strategies are used in Vue.extend()
.
Conclusion
Mixins allow us to create code that can be included in multiple components.
They’re merged by taking component’s methods
, components
, and directives
taking precedence over the mixin’s items in these properties.
Data are merged together as well, with component’s data taking precedence if the same key exists in both places.
Hooks are merged into an array, with the mixin’s hook being called before the component’s hook if they have the same name.