Why your Vue code sucks

Thursday, Nov 24, 2022 by Anthony Gore

I love Vue because it makes it easy to create high-quality, maintainable UI code without too much effort.

To gain these benefits, though, it is important that you following the key patterns and design principles.

If your Vue code has become unwieldy and difficult to maintain, you may have found yourself stuck in one these common anti-patterns:

  • Not using events for component communication
  • Allowing components to grow too large
  • Overusing mixins

In this article, we'll take a look at these common anti-patterns and see why they lead to Vue code that sucks. We'll also see what you can do to fix them.

Anti-pattern #1: not using events for component communication

You may have heard that with libraries like Vue, props send data "down" while events send data "up". Most coders are happy to use props, but may try to avoid events.

Let's see why this leads to bad code. Say you have a component structure consisting of two components - a parent and a child. The parent declares the Child in its template like so:

Parent.vue

<template>
<Child />
</template>

<script setup>
import Child from './Child.vue'
</script>

Let's also say the parent has a method doSomething that the child wants to trigger. This is often the case if the child wants the parent to reload data, for example.

Parent.vue

<script setup>
import Child from './Child.vue'

function doSomething() {
//
}
</script>

This is where we should use an event. However, some developers are tempted to skip that step and insteda use the $parent property of the component to access the parent's method:

Child.vue

// Bad.
this.$parent.doSomething();

This seems like simple and easy to read code. Why is it bad?

Let's go over the issues:

Issue 1: coupling

Having the child call the parent means this child cannot be used by a different parent and vice versa. Therefore, these components must always be used together.

Good component design means making our components independent and reusable. Doing so means letting each component handle its own logic and state.

Issue 2: maintenance

When a child component calls a parent component method, you can run into trouble if the parent component changes the name of the method or its profile.

For example, what if the parent renames doSomething to doSomethingElse? Your IDE won't alert you of the issue and you'll get a runtime error:

this.$parent.doSomething();
// error: doSomething is not defined

Issue 3: debugging

When you go to debug a component, you will often track the state of that component by following logic through the component and observing methods and events that change the state.

When your component logic is spread across multiple components this becomes very difficult as you don't know where state changes are originating from.

Correct way: using an event

To avoid all the above problems, a child cannot directly trigger it's parent's method. Instead, the child should trigger an event that parent can listen to, and let the parent decide what to do:

Child.vue

// Good.
defineEvents(['my-event'])

this.$emit('my-event')

Parent.vue

<template>
<Child @my-event="doSomething" />
</template>

Using events will ensure your components are isolated and reusable, easy to maintain, and easy to track state.

This article is still being completed, stay tuned for the rest!