5 Principles for Writing Clean and Maintainable Vue.js Code

5 Principles for Writing Clean and Maintainable Vue.js Code
5 Principles for Writing Clean and Maintainable Vue.js Code Write code that others will understand and love

There’s a lot of power in clean code. Clean code might not become better over time, the same way that a fine wine would, but at least it remains understandable as time goes by.

Spaghetti code, on the other hand, the more time goes by, the worse it becomes. You might be able to handle the chaos for now, but what if you work on something else and come back to it three, five, or 20 months later?

One of the main principles I like to stick to as a programmer, including in Vue.js projects, is the DRY principle.

DRY stands for don’t repeat yourself, and its goal is to reduce repetition.

The reason why I insist it’s important is that WET _(write everything twice) _solutions tend to introduce additional complexity in many cases. I’ll share a simple example to help you understand.

Let’s say you have duplicate code in two places in your code base. What if you decide to change the logic or some of the parameters? You’ll have to refactor your code in both places. How bad can that be, though?

  • What if you change it in one place and forget the other?
  • What if you end up having the same code in five places?
  • What if you don’t work on the project and come back to it after five months?

Keeping your code clean can protect you from many issues, and it can also save you a lot of time. It might not seem obvious in the short term, but in the long term, clean code is the far superior option in terms of productivity.

Use Mixins

The main way to create reusable methods, data attributes, computed properties, and more in Vue is to create mixins.

If you’re reusing logic across components, it makes sense to define it inside mixins that you can use throughout your application.

Let’s say your application has multiple routes, and by using vue-router, you connect each route with a corresponding component.

- App.vue
-- Home.vue
-- Analytics.vue
-- Reports.vue

And now let’s assume you’ve created a custom product tour that you want to start whenever a user enters any of the tabs.

If you’re not using mixins and you want the product tour to appear on all pages, you’ll need to copy the same code into each mounted callback and the startProductTour method as well. I’ve written the Home.vue example here, but you’d need the same code inside Analytics.vue and Reports.vue.

Home.vue

export default {
  mounted() {
    this.startProductTour();
  }.
  methods: {
    startProductTour() {
      // start product tour actions here
    }
  }
}

But if you define the above as a mixin…

ProductTour** mixin**

export default {
  mounted() {
    this.startProductTour();
  }.
  methods: {
    startProductTour() {
      // start product tour actions here
    }
  }
}

…then you can use it in all of your components, wherever it’s needed with a single line of code. And if you change the product, you only need to update your mixin once instead of editing multiple files.

Home.vue

import ProductTourMixin from '../mixins/ProductTourMixin';
export default {
  mixins: [ProductTourMixin]
}

In this case, your new project structure would look like this:

- App.vue
- mixins/ProductTourMixin
-- Home.vue
-- Analytics.vue
-- Reports.vue

By using mixins, you’ve saved time and effort and have avoided headaches — just by keeping things clean and DRY.

Don’t Be Cheap With Components

In one of my first Vue applications, my components would grow to have hundreds or even thousands of lines of code. My giga components would do everything inside, including a massive HTML <template>, many methods, and a lot of JavaScript within those methods. The problem with that is:

  • Everything I was building had no reusability. If I wanted to reuse a part of my giga component, I’d need to copy it into a new component leading to duplication.
  • The giga component was hard to read and maintain and would only become longer over time if I didn’t refactor
  • It was hard to split work among people without causing conflicts, which led to even more time required to deploy functional code

The point is clear I believe — creating one, or a few, large components instead of many small ones can be an absolute nightmare.

So what are the benefits of creating many small components instead of fewer large ones?

  1. Small components are often reusable, saving you time and also protecting you from duplicate code
  2. You are, for the most part, decoupling the parent- and child-element logic. If you make changes to the child component, it won’t affect the parent element. The same is true for the parent element as well, as long as you don’t change the props being passed down in any way.
  3. Smaller components come with performance benefits in large v-for loops because Vue won’t update them unless the props change (for nonobject props).

In a large application, it’s necessary to divide the whole app into components to make development manageable.

This quote is from the official Vue documentation on components, and it states the importance of splitting your app into components.

**Note: **Reading the entire documentation of your favorite frameworks and technologies can reveal a treasure trove of information, so I suggest you do it frequently.

Validate Your Props

Validating your props is an important part of making your Vue application maintainable.

First, let’s look at the wrong way of defining props in a component.

export default {
  props: ['myProp'],
}

This way of defining props is only acceptable during development, but before going live, you should write full definitions for your props. At the very minimum, you should define a type for your props.

// quick way
export default {
  props: {
    myProp: String,
  }
}
// this should be preferred
export default {
  props: {
    myProp: {
      type: String
    }
  }
}

But the best way is to fully define your props and validate them.

props: {
  myProp: {
    type: Number,
    required: true,
    validator: (value) => {
      return value > 0;
    }
  }
}

By validating your props, you make sure to avoid issues that are related to passing unexpected values, and it’ll help you catch such errors during development.

As an extra advantage, by having well-defined props, you’ll always be able to look at your prop definitions and validations to understand what values are expected. Think of it as a minidocumentation of what the component expects. Sometimes it’s better to have well-defined inputs, with the right names semantically than a ton of comments.

Once again, clean code beats sloppy code, allowing you to avoid errors and remember the purpose of the prop at any time.

Interact With APIs Using Services

Vue doesn’t have services like Angular, which allows you to create services to interact with APIs and build common application logic.

In my experience, it pays off to decouple presentation logic from application logic as much as possible.

To better illustrate my idea, I’ll share an example. Let’s say you have a Reports component that includes three child components: ReportTableReportChart, and ReportStats.

Reports.vue

<!-- This example is intentionally kept simple -->
<template>
  <report-stats :stats="stats" />
  <report-chart :chartData="chartData" />
  <report-table :breakdown="breakdown" />
</template>
<script>
export default {
  data() {
    return {
      stats: null,
      chartData: null,
      breakdown: null
    }
  }
}
</script>

This is the foundation of your Reports component. Now you’d need a way to get the data from your API, so you’d have something like this:

mounted() {
  this.getStats();
}

And then you’d have something like this:

methods: {
  getStats() {
    axios.get('/api/to-get/the-data')
      .then(response => {
        this.stats = response.data.stats
      });
  }
}

Now that’s definitely not bad, and in smaller applications, it may actually be fine. But decoupling the application logic from the presentation logic has multiple benefits:

  • You could use the same services in a backend environment or in a different frontend environment without using Vue
  • You keep your Vue code clean and understandable. Your presentation layer doesn’t need to know the full complexity of your application.
  • You have a well-defined API, sort of like a precontroller that can help you and your team understand what parameters are required and how each call works.

So how do we turn our example into a service?

ReportsService.js

const ReportsService = {
  stats: {
    index: async (/* params */) => {
      const response = await axios.get('/api-link');
      return response.data;
    }
  }
}
export default ReportsService;

Reports.vue

import ReportsService from '../services/ReportsService';
export default {
  methods: {
    async getStats() {
      this.stats = await ReportsService.stats.index(/* params */);
    }
  }
}

Using simple logic, we created a service that’s able to interact with our backend API or implement application logic. Our Vue component, then, is able to interact with our service, get the data it needs, and pass it to the component, _without _knowing any more details about our API or application logic.

Follow the Style Guide

It’d be beyond the scope of this article to go deep into the Vue style guide. In general, it’s important to learn how the frameworks you’re using are designed and how they expect you to work with them.

The creators of the framework have the right experience to make style guidelines, but since the community is likely to follow the same guidelines it’s important to follow them as well for consistency.

Some of the most important style-guide rules are the following:

Use the data() function instead of a data object

This is the wrong way of doing things:

export default {
  data: {
    value: 1,
  }
}

And this is the right way:

export default {
  data() {
    return {
      value: 1,
    }
  }
}

The reason why you must use a function and not an object is that using an object would make the data property mutable by all of the instances of the same component.

Always key v-fors

All your v-for loops must have a key passed down because it helps Vue render your lists more efficiently.

<my-item
  v-for="item in items"
  :key="item.id"
  :item="item" />
<!-- or with HTMl elements -->
<div
  v-for="item in items"
  :key="item.id"
>
  {{ item.value }}
</div>

Don’t use v-if on the same element as v-for

The documentation is pretty clear as to why this is a bad idea. Not following this has negative outcomes on performance — as Vue will need to rerender the entire list, even when the items array hasn’t changed.

I’ll explain what you can do alternatively and why it’s better.

Instead of…

<div v-for="item in items" v-if="item.shouldShow()">
  {{ item.value }}
</div>

…you should do the following:

<div v-for="item in visibleItems">
    {{ item.value }}
</div>

And add this to your computed properties:

visibleItems() {
  return this.items.filter(item => {
    return item.shouldShow();
  });
}

The benefits of doing this are the following:

  1. The computed property visibleItems is only reevaluted when there are changes in the items array.
  2. The v-for has to only process the visibleItems array, instead of the entire items array, which makes rendering more efficient.
  3. You maintain a cleaner template with less logic inside, which is always good. Remember, decoupling logic from your presentation layer keeps things clean and maintainable.

For further reading on the style guide, you can check out the official section of the Vue documentation. Even if you’re an experienced Vue developer, you may have a lot to learn from reading it.

Conclusion

Writing clean and maintainable code will make life easier for whoever works with you. But it will also make your own life better as you’ll have fewer spaghetti-code messes to deal with.

If you’re in a position where you’re doing code reviews, you should take a lot of the above into account to make sure your team is deploying high-quality code and maintaining high productivity in the long term.

Thank you for reading this far!

Suggest:

Learn Vue.js from scratch 2018

Is Vue.js 3.0 Breaking Vue? Vue 3.0 Preview!

Vue.js Tutorial: Zero to Sixty

Learn Vue.js from Scratch - Full Course for Beginners

Create Shopping basket page With Vuejs and Nodejs

Learn Vue 2 in 65 Minutes -The Vue Tutorial for 2018