Editor’s Note: This blog post was updated 7 August 2022 to include sections on why you should use mixins in Vue and the drawbacks of doing so.
If you are a Vue lover (like me) and are looking for a way to extend your Vue application, you’ve come to the right place. Vue mixins and directives are a powerful combination and a great way to add more reusable functions across parts of your application.
If you are from an object-oriented programming background, you’ll see Vue mixins as an imitation of parent classes. You will also see that directives are similar to helper functions.
If you do not have an OOP background, then think of mixins as a utility that you design to be shared by multiple people. If you are thinking about an office, it would be the photocopier. If you are thinking about a mall, it would be mall security. Basically, mixins are resources that multiple parts of your application share.
Below are a few prerequisites that you’ll need before moving forward in this article.
The Vue documentation has a really simple and straightforward explanation for what mixins are and how they work. According to the docs, mixins are a flexible way to distribute reusable functionalities for Vue components. A mixin object can contain any component options. When a component uses a mixin, all options in the mixin will be “mixed” into the component’s own options.
In simpler terms, it means that we can create a component with its data, methods, and life-cycle components, as well as have other components extend it. Now, this is different from using components inside other components where you can have a custom component with a name like <vue-custom></vue-custom>
inside of your template.
For example, we can build a normal Vue component to hold basic configurations for our app, such as:
Mixins allow us to reuse functionalities and logic within our Vue components. This means we can use the same logic or functionality in multiple components without having to manually rewrite the logic in each one.
This provides us a level of flexibility and allows us to reduce code duplication, making sure we abide by the popular DRY (Don’t Repeat Yourself) principle.
One other thing that makes mixins important is that they don’t have any effect outside the defined scope.
Let’s create a simple mixin:
<template> <div> <div>{{title}}</div> <div>{{copyright}}</div> </div> </template> <script> export default { name: "HelloWorld", data() { return { title: 'Mixins are cool', copyright: 'All rights reserved. Product of super awesome people' }; }, created: function() { this.greetings(); }, methods: { greetings: function() { console.log('Howdy my good fellow!'); } } }; </script>
Interestingly, we can refactor the logic in this component with mixins. This comes in handy in cases where you need to repeat this exact logic in multiple components.
Let’s create a simple mixin in a myMixin.js
file within our project:
export const myMixin = { data() { return { title: 'Mixins are cool', copyright: 'All rights reserved. Product of super awesome people' }; }, created: function() { this.greetings(); }, methods: { greetings: function() { console.log('Howdy my good fellow!'); } } };
Okay, that’s as simple as it gets for a mixin. Now, if we use this in our component, you will see the magic in it.
And to use this, we can import it and do the following in our template:
<template> <div> <div>{{title}}</div> <div>{{copyright}}</div> </div> </template> <script> import myMixin from "/myMixin"; export default { name: "HelloWorld", mixins: [myMixin] }; </script>
One important thing to note is that there are two types of mixins – global and local.
Local mixins are what we’ve explained above with the myMixin.js
file. The mixin is defined in an individual .js
file and imported for use within individual components in our Vue project.
On the other hand, global mixins allow us to do even more. Similar to local mixins, we also have our myMixin.js
file. This time, we import it directly into our main.js
file, making it globally available to all components within our project automatically.
For instance, once we’ve created our myMixin.js
file, we go to our main.js
file and import it as shown below:
import { createApp } from 'vue' import App from './App.vue' import myMixin from './myMixin' const app = createApp(App); app.mixin(GlobalMixin); app.mount('#app')
Now, any component within our Vue component can access the functionality in our mixin file without needing to import individually.
Directives are methods like v-for
that we can create to modify elements on our template. You know how v-if
hides a component if a condition is not met? How about if we underline a long sentence with a directive?
We can even change the text a little as a way to highlight it. We can have global directives that we register so that all of the components in our Vue application can use them. We also have local directives that are specific to that particular component. Awesome, right?
Let’s create a global directive in our main.js
now.
Register a global custom directive called v-highlight
:
// It's app.directive in Vue 3 Vue.directive('highlight', { // When the bound element is inserted into the DOM... // Use "mounted" instead of "inserted" in Vue 3 inserted: function(el, binding) { // set the colour we added to the bind el.style.backgroundColor = binding.value ? binding.value : 'blue'; el.style.fontStyle = 'italic'; el.style.fontSize = '24px'; } });
Here, we’re changing the style of the element attached to this directive. Plus, if there’s a color attached as a value
to the directive, we set it as the background color. If not, we set the background color to blue
.
Now, if we use this directive, you should see that parts of the text have changed.
To use this, we can do the following in our template:
<template> <div> <p v-highlight>Hello There!</p> <p v-highlight="red">This is a red guy</p> </div> </template>
This is another customization helper we will look at. Filters help us in many ways (you might get angry that you didn’t know about these earlier if this is your first time encountering them). We can define filters globally or locally, just like directives.
Filters can be used to apply common formatting to text or heavy filtration to an array or object. They are JavaScript functions, so we can define them to take as many arguments as possible. Also, we can chain them and use multiple filters as well. Cool, right?
Let’s define a simple filter to capitalize the first word of the body of text (this is really useful when displaying things like names supplied by your user):
Vue.filter('capitalize', function(value) { if (!value) return ''; value = value.toString(); return value.charAt(0).toUpperCase() + value.slice(1); });
And to use this, we can do the following in our template:
Now, anywhere we use this filter, the first character will always be capitalized.
N.B., Filters are still applicable in Vue 2 but have been deprecated in Vue 3.
We are going to compose a simple Vue application using everything we’ve learned.
First, let’s define our mixin:
const myMixin = { data() { return { title: 'mixins are cool' }; }, created: function() { alert('Howdy my good fellow!'); } };
Then we define our directive in our main.js
:
Vue.directive('highlight', { inserted: function (el, binding) { el.style.color = binding.value ? binding.value : "blue" el.style.fontStyle = 'italic' el.style.fontSize = '24px' } })
Now, let’s define our filter in our main.js
:
Vue.filter('capitalize', function (value) { if (!value) return '' value = value.toString() return value.charAt(0).toUpperCase() + value.slice(1) })
Finally, the simple template to see if these things actually work:
<template> <div id="app"> <p v-highlight>{{title | capitalize}}</p> <p v-highlight="'red'">This is a red guy</p> <p>{{'this is a plain small guy' | capitalize}}</p> <div> </template> <script> export default { name: "HelloWorld", mixins: [myMixin] }; </script>
And that’s it!
Vue 3 now provides other means of sharing functionalities that provide a better developer experience, such as using composables.
Mixins are still as efficient while working with the Options API, but you might experience some drawbacks, including:
While working with mixins, there’s a higher chance of having conflicting names within our components. This might be a challenge, especially in cases where a new developer is inheriting a legacy codebase and they’re not exactly familiar with the existing property names within the mixin.
This can end up causing unwanted behaviors in our Vue app.
Similar to the potential naming issues I’ve highlighted above, it’s somewhat stressful for a new developer to figure out mixins and how they might be affecting the components, especially if they’re dealing with global mixins.
In general, figuring out all the functionalities within the components can be difficult.
Everything we mentioned here comes in handy when building applications that are likely to grow in complexity. You want to define many reusable functions or format them in a way that can be reused across components, so you do not have to define the same thing over and over again.
Most importantly, you want to have a single source of truth. Dedicate one spot to make changes.
I’m excited by the thought of the cool things you can now build with these features. Please do share them with me.
Debugging Vue.js applications can be difficult, especially when there are dozens, if not hundreds of mutations during a user session. If you’re interested in monitoring and tracking Vue mutations for all of your users in production, try LogRocket.
LogRocket is like a DVR for web and mobile apps, recording literally everything that happens in your Vue apps, including network requests, JavaScript errors, performance problems, and much more. Instead of guessing why problems happen, you can aggregate and report on what state your application was in when an issue occurred.
The LogRocket Vuex plugin logs Vuex mutations to the LogRocket console, giving you context around what led to an error and what state the application was in when an issue occurred.
Modernize how you debug your Vue apps — start monitoring for free.
Would you be interested in joining LogRocket's developer community?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowLearn how to implement one-way and two-way data binding in Vue.js, using v-model and advanced techniques like defineModel for better apps.
Compare Prisma and Drizzle ORMs to learn their differences, strengths, and weaknesses for data access and migrations.
It’s easy for devs to default to JavaScript to fix every problem. Let’s use the RoLP to find simpler alternatives with HTML and CSS.
Learn how to manage memory leaks in Rust, avoid unsafe behavior, and use tools like weak references to ensure efficient programs.