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!
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.
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: '[email protected]', 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.
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.
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.
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!
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!
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!
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.
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.
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.
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.
Hey there, want to help make our blog better?
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 nowMaking carousels can be time-consuming, but it doesn’t have to be. Learn how to use React Snap Carousel to simplify the process.
Consider using a React form library to mitigate the challenges of building and managing forms and surveys.
In this article, you’ll learn how to set up Hoppscotch and which APIs to test it with. Then we’ll discuss alternatives: OpenAPI DevTools and Postman.
Learn to migrate from react-native-camera to VisionCamera, manage permissions, optimize performance, and implement advanced features.
6 Replies to "Cleaning up your Vue.js code with ES6+"
Great tips, one question, when I use the arrow functions I get undefined on this… Any suggestion?
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
(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.
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
Got it! Thanks for the clarification and explanation!
Nice, practical write-up, thanks!