Cleaning up your Vue.js code with ES6+

4 min read 1298

Cleaning Up Your Vue.js Code With ES6+

ES6 was a big step forward for the web, and it introduced many new features that solve various pain points that exist for all JavaScript developers. But a few of its features are specifically suited to solve problems that arise when developing with Vue.js. This article will cover four of those Vue-specific features. We’ll look at how each feature works and what problem it solves for your apps and websites. Without further ado, let’s dive in!

Feature #1: Method definition shorthand

This first feature I want to talk about has a purely aesthetic effect, but it really helps to make your code as readable as possible. ES6 introduced this shorthand to more succinctly assign functions to objects, which we do all the time in Vue for methods, computed properties, watchers, and lifecycle methods. Here’s an example of how you can apply it to your Vue code:

// Without shorthand
{
    methods: {
        getValue: function() { // ... }
    },
    computed: {
        halfValue: function() { // ... }
    },
    created: function() { // ... }
}

// With ES6 shorthand
{
    methods: {
        getValue() { // ... }
    },
    computed: {
        halfValue() { // ... }
    },
    created() { // ... }
}

Again, this is a small change, but it can make a big difference for readability.

LogRocket Free Trial Banner

Feature #2: Destructuring

Destructuring is a feature added in ES6 that makes it easier to pull properties out of an object and assign them to a variable. Before we get into how this helps us in our Vue code, here’s a very basic example of how object destructuring works:

const person = { name: 'Jake', email: 'jake@example.com', phone: '555-555-5555' }

// With destructuring
const { name, email, phone } = person

// Without destructuring
const name = person.name
const email = person.email
const phone = person.phone

The two examples above (with/without destructuring) work exactly the same. The version using destructuring is just a cleaner code pattern to achieve the same result.

So how can you use destructuring in your Vue codebases? There are two main areas where destructuring shines in Vue: destructuring properties from this, and receiving props from scoped slots. Let’s walk through each of those use cases.

Destructuring from this

In Vue, to reference data, methods, or anything on the Vue or your component instance, you use this. But sometimes it’s nice to access those instance properties without referring to this over and over again. Let me show you a nice little trick to pull properties from this into your local function scope:

data() {
    return {
        endpoint: 'example.com/api',
    }
},
methods: {
    postForm() { // this is just an example method we can call in submitForm }
    submitForm() {
        // Without destructuring
        const endpoint = this.endpoint
        const postForm = this.postForm

        // With destructuring
        const { endpoint, postForm } = this
  }
}

This pattern allows us not only to use these variables without the this prefix, it also gives us clarity on which pieces of data and/or methods that our function relies on.

Scoped slots

Slots allow us to pass templates into our components, and scoped slots allow our components to provide some component data to those templates. If you’re not familiar with scoped slots, this might not make as much sense, but hopefully this example can at least reinforce how destructuring works and how you can use it in many different scenarios:

<!-- Without Destructuring -->
<User v-slot="slotProps">
    <div>Name: {{ slotProps.name }}</div>
    <div>Email: {{ slotProps.email }}</div>
</User>

<!-- With Destructuring -->
<User v-slot="{ name, email }">
    <div>Name: {{ name }}</div>
    <div>Email: {{ email }}</div>
</User>

Not unlike the “destructuring from this” pattern, not only does destructuring our slot props allow us to access our variables without using the slotProps prefix, but it shows us exactly what properties we are accepting through the slot.

Feature #3: Functional array methods

ES6 introduced many new methods built into the Array prototype. These methods allow you to interact with the data in your arrays in different ways, like transforming each item (map), sorting an array, or filtering an array. My favorite array methods that I use commonly in Vue apps are filter, map, forEach, and includes. Here’s an example using filter:

computed: {
    // Without "filter" functional array method
    oldFilteredItems() {
        const filtered = []
        for (const item in this.items) {
            if(item.value > 10) {
                filtered.push(item)
            }
        }
        return filtered
    },
    // With "filter" functional array method
    filteredItems() {
        return this.items.filter((item) => item.value > 10)
    }
}

This reduces the code we have to write (and read!) from seven lines to only one!

Feature #4: Arrow functions

Before we learn about arrow functions, how they work, and how to use them in your Vue code, let’s look at the problem they solve. Check out the following code:

data() {
    return {
        scrolled: false
    }
},
mounted() {
    window.addEventListener('scroll', function() {
        this.scrolled = true
    })
}

This code doesn’t work. Why? Because when you create a new function, the value of this is re-bound to equal the function instance instead of the Vue instance. If you’ve ever run into this problem, you may have tried the following approach to fix this problem:

mounted() {
    var self = this
    window.addEventListener('scroll', function() {
        self.scrolled = true
    })
}

While this does “fix” the problem, it is definitely not ideal to have var self = this littered around your code, especially when this is a solvable problem with (drumroll please) … arrow functions!

Arrow functions are very similar to standard functions, but one key difference is that arrow functions don’t re-bind this, which is very helpful in Vue apps! Here’s an updated version of the earlier example, wherein we’ve replaced the standard function with an arrow function so this doesn’t get re-bound:

mounted() {
    window.addEventListener('scroll', () => {
        this.scrolled = true
    })
}

Here is a rule that I find helpful to follow when writing Vue apps: within Vue components, this should always refer to the Vue instance. This isn’t hard to achieve if you use arrow functions, and it makes your code easier to understand.

If you’re not familiar with arrow functions, they’re definitely worth learning. While they’re especially useful in this scenario, they also allow you to write much more succinct functions, which is applicable to many more scenarios. One other place they’re beneficial is paired with array methods! If you look at my filteredItems function in Feature #4, you can see I used an arrow function as the first argument of the filter array method!

Wrapping up

Before I sign off, I want to talk about how I went about identifying these four improvements, and how you can learn to spot places that could use improvement in your codebase(s). Here are a few tips!

Look for repetition

Not all repetition is bad, but seeing anything repeated throughout your code should have you wondering if there is an opportunity for a good abstraction, or to learn a new pattern or language feature that solves the problem you’re working around.

Be aware of language changes

It would be impossible to know that you can simplify many of the loops in your code by using array methods if you didn’t keep up with the changes to JavaScript. That said, you don’t have to “dive deep” into every new thing, but try to have an awareness of what is available in the language you’re working with. Then, when you encounter a problem, hopefully you will be reminded of a language feature that solves the problem you’re facing.

Read other people’s code

If you work on a team, ask to review someone else’s code with them, or ask them to review yours. Seeing other people’s code, or their comments on yours, will allow you to learn how others do things differently. And when you see a code pattern you don’t recognize, figure out what it is and, if it makes sense, apply it to your code.

Plug: , a DVR for web apps

LogRocket is a frontend application monitoring solution that lets you replay problems as if they happened in your own browser. Instead of guessing why errors happen, or asking users for screenshots and log dumps, LogRocket lets you replay the session to quickly understand what went wrong. It works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store.

In addition to logging Redux actions and state, LogRocket records console logs, JavaScript errors, stacktraces, network requests/responses with headers + bodies, browser metadata, and custom logs. It also instruments the DOM to record the HTML and CSS on the page, recreating pixel-perfect videos of even the most complex single-page apps.

.

Further reading


6 Replies to “Cleaning up your Vue.js code with ES6+”

  1. Great tips, one question, when I use the arrow functions I get undefined on this… Any suggestion?

    1. Hi, Maximiliano, thanks for reading!

      So, if “this” is returning “undefined”, you’re probably using arrow functions in the wrong place. You shouldn’t use them when defining a function for your data, or lifecycle methods, as you do want this to be bound to the context. So doing { mounted: () => { console.log(this.hello) } } will console log undefined (rightly).

      You should use arrow functions _within_ your methods, lifecylcles, etc. so that the context of “this” will always be your component.

      For more information on arrow functions, check out this article: https://codeburst.io/javascript-arrow-functions-for-beginners-926947fc0cdc

  2. (hello = 0) Destructuring works let { hello } = this and then I hello = 1 in the method and the value changes if I assign it to a different value but when a different method later calls the data() for that particular data it is as-if it was never changed at all because when I check it again it is still hello = 0.

    1. Hi, Lou!

      So the reason mutating the value of “hello” doesn’t work, is that primitive values (like a number or string) are copied by value, not reference. When you do let { hello } = this, what it’s really doing is let hello = this.hello, which *copies* the value of this.hello into a new local variable. So when you mutate your local variable, it won’t change the value of this.hello.

      For more information, check out this AWESOME article on value vs reference in JS: https://codeburst.io/explaining-value-vs-reference-in-javascript-647a975e12a0

Leave a Reply