Vue gotchas

Clean up after yourself

Most of the times we use Vue with third-party plugins. Let’s say we want to mount a color picker library into our component. That usually means, we have to import the module and initialize it in the mounted() hook. It might look like this.

The mounted hook gets called when our component root element (accessible as this.$el) has been mounted to DOM. After that, we are able to access our referenced elements and manipulate them or, as in this instance, use them to initialize a color picker.

The gotcha with this is that after our component is destroyed, the ColorPicker instance is not. At least not properly. The reference to it is lost and hopefully garbage-collected, but since the time it was created, ColorPicker might have attached a few event listeners in our DOM which still exist.

All well-written libraries should provide an API method to destroy their initialized instances and clean up after themselves. We should check the documentation and confirm the library includes a method that does it. If it doesn’t, it’s time to find a different library.

All we need to do next is call this method in the beforeDestroy() hook.

beforeDestroy () { this.colorPicker.destroy()}

This also applies to timeouts and intervals. Don’t forget to call clearInterval() or clearTimeout() in your beforeDestroy() hook

To demonstrate this problem, I created a fiddle: https://jsfiddle.net/Horsetoast/nz5uh9ed/7/Each component you add creates an event listener but doesn’t remove it.Create a bunch, remove them and click anywhere in the window to see what’s happening.

The :key to success

If you’ve used Vue before you’ve most likely encountered this warning message

component lists rendered with v-for should have explicit keys

Why do we need to pass a key to v-for elements? To cite the documentation

When Vue is updating a list of elements rendered with v-for, by default it uses an “in-place patch” strategy. If the order of the data items has changed, instead of moving the DOM elements to match the order of the items, Vue will patch each element in-place and make sure it reflects what should be rendered at that particular index.

Why is this important? Because things can go wrong if we use “index” as the key.

When we shuffle the array of users now, the user names will get updated as they should but the inputs will stay in their original positions with the same values.

To solve this problem, we would pass a unique id to the key directive to tell Vue that we want to track our elements not by indices, but by ids. In order for this solution to work, we assume that our user objects contain an id property.

Of course, if you bind the input to data with v-model, everything will work just fine. This example is just to demonstrate the caveat. Instead of inputs, you might have more complex components that have attached elements from a library that operates on DOM nodes in which case re-ordering the array could mess things up.

Using keys with transitions

I have often encountered a scenario when using the transition component for animations that use the same tag name.

I remember the first time I was confused as to why my transition was not working:

When toggling between elements that have the same tag name, you must tell Vue that they are distinct elements by giving them unique key attributes. Otherwise, Vue’s compiler will only replace the content of the element for efficiency. Even when technically unnecessary though, it’s considered good practice to always key multiple items within a component.

However, there are cases where we can’t initialize the data beforehand because we don’t know its structure (Some people might object that if we can’t predict our data structure then we’re doing something wrong, but let’s not dive into that discussion now). Imagine a scenario where we need to fetch the data of our users which comes as an array of objects.

{ "id": "p18Af0", "name": "Chris"},{ "id": "lqR55z", "name": "Sarah"}

But right after we fetch them, we want to store them as an object using ids as the keys and names as the values. Why store them in this way? Maybe we’re trying to be efficient and have a map of users instead of an array. Therefore we don’t need to loop through it every time we search for a user by id.