Menu Close

Making Code Reusable with Vue.js Mixins

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.

Posted in vue