Photo by wayX from Pexels

Vue.js is a JavaScript library that spun off AngularJS. It’s a lightweight framework used for building front-ends in single-page applications (SPAs).

There is an introduction on the official website, and if you want to learn about Vue.js, you should probably start by reading that. There are a few things I would have liked to see better explained, though, so this article is my own attempt at unpacking the framework.

The Getting Started section of the Vue.js introduction provides the following code for including Vue.js in your web page:

<!-- development version, includes helpful console warnings --> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>

After adding this to the <header> of your page, when you view your page in Google Chrome and open developer tools, the following message should be waiting in the console:

Download the Vue Devtools extension for a better development experience: https://github.com/vuejs/vue-devtools

Following the GitHub link takes you to the Vue.js devtools Chrome extension in the Chrome Web Store. However, after installing this extension and loading your page, the extension might complain that it cannot find Vue.js.

Vue.js not detected

Firstly, this could be because your page is a local .html file opened via the file:// protocol. In this case, the extension needs to be allowed access to file URLs (as per the “Common problems and how to fix” section of the GitHub readme file). This is done by visiting chrome://extensions/ and toggling the relevant switch on the Vue.js devtools extension.

Secondly, the extension icon won’t light up until your page has created a Vue instance and bound it to an existing HTML element, e.g.

<main id="app"></main><script>
var app = new Vue({
el: "#app"
});
</script>

Only once code like the snippet above has run on your page can you make use of the newly added Vue tab of the Chrome developer tools.

The Vue tab in Chrome developer tools

This tab lets you inspect your Vue instances and their data live.

This is the typical structure of a Vue.js instance:

var vm = new Vue {
el: '#app',
data: {
field1: false,
field2: 99,
field3: ['shirt', 'pants', 'socks']
},
methods: {
doSomething: function() {
// do something
},
doSomethingWithParam: function(param) {
// do something with param
},
computed: {
oneMore: function() {
return this.field2 + 1;
},
yesNo: function() {
if (this.field1) {
return "Yes";
} else {
return "No";
}
}
}
}

Note the different sets of fields:

  • Element (el): This is a selector that identifies the element that will act as a template for this Vue instance. It’s typically the ID of the element, e.g. #app, but it could be any other CSS selector.
  • Data (data): These are the fields that are being watched, i.e. the template will be updated whenever one of them has its value changed. These can be numbers, strings, objects, anything!
  • Methods (methods): These are methods you can invoke in your templates, and are typically bound as event handlers.
  • Computed (computed): These fields are used in your template just like data. The difference is that you can specify how these are calculated.

The HTML element that becomes the template (bound using el) can reference fields from the data, methods and computed sections of the Vue instance using double curly braces, e.g. {{ item.price }}. Vue.js will update the relevant parts of the HTML whenever the underlying data changes.

For example:

<tr id="app">
<td>{{ name }}</td>
<td>{{ price }}</td>
</tr>

The Vue instance will populate the cells in the table row above using the name and price fields from its data property. If the values of these fields change, the cells are updated automatically.

You can also do things like bind events and use control structures. Read on for some examples (or check the link to my source code at the end).

A Vue instance (including components — see below) can declare computed fields, which are basically functions that return values for the template, e.g.

Vue.component('cart-counter', {
props: ['cart'],
computed: {
totalItems: function () {
var total = 0;
for (var i = 0; i < this.cart.items.length; i++) {
total += this.cart.items[i].quantity;
}
return total;
}
},
template: '<span class="num-items">{{ totalItems }}</span>'
});

Computed fields are referenced in the same way as data fields.

Vue.js allows you to assign methods as event handlers using the directive v-on. Directives are HTML attributes that have special meaning to Vue.js. For example, you can assign a click event handler by adding the v-on:click attribute on an element.

<div class="cart" v-on:click="showCartContents">

When the user clicks on this div, Vue looks up the showCartContents function from the Vue instance’s methods property.

Note that routing the click event handler to a method on your Vue instance doesn’t stop you from accessing the event parameter. For instance, you can do this in your method:

methods: {
showCartContents: function(event) {
event.stopPropagation();
this.cartOpen = true;
}
}

If you want to pass your own parameters to the method as well, you need to pass the event object explicitly. You do this using $event, e.g.

<img src="cross.svg" v-on:click="removeFromCart($event, item)" />

Note that you don’t actually need to do this to stop event propagation. Vue.js has several convenient Event Modifiers, among them .stop, which is used for precisely this purpose, e.g.

<div class="cart" v-on:click.stop="showCartContents">

Directives can also be used to add if-statements and loops to templates. The following template renders one message if items.length is greater than 0 and another if the array is empty:

<div v-if="items.length > 0">
<p>You have stuff in your cart.</p>
</div>
<div v-else>
<p>Your cart is empty!</p>
</div>

Given an array of items, the following template renders an unordered list with the name of each item in a <li> element.

<ul>
<li v-for="item in items">{{ item.name }}</li>
</ul>

It doesn’t look great when the template markup is displayed on your web page before Vue.js has a chance to load. The v-show directive allows you to conditionally display parts of your HTML template, but it doesn’t take effect until after Vue.js has loaded.

This is where the v-cloak directive is handy. This attribute is automatically removed by Vue.js as soon as the library has loaded, so you can use it in combination with some CSS to hide certain elements until Vue is ready, e.g.

<p v-cloak>Vue.js has loaded!</p>

If you add the following to your stylesheet, the <p> element above will stay hidden until Vue.js is done loading:

[v-cloak] {
display: none;
}

You can register a Vue instance as a component. Your component can then be invoked by using its name as an HTML element, e.g. <my-component> (compare this to binding Vue instances one-by-one using element IDs). For example:

Vue.component('cart-counter', {
props: ['cart'],
template: '<span class="num-items">{{ cart.totalItems() }}</span>'
});

Use the props array to specify the names of attributes that can be set on your component. These are automatically bound to fields in the component Vue instance. For example:

<cart-counter cart="cartObject"></cart-counter>

Vue.js will replace this element with the HTML from the template, calling the totalItems() method on the cartObject instance that was passed in to determine the value for the <span>.

It’s easy to go overboard with Vue.js components. Early on, I found myself trying to reference the computed property of a child component in another Vue instance.

I had to remind myself that Vue is just that — a view. It’s not a container for logic, which belongs in a dedicated object that is simply referenced by the Vue instance. For example, the cart counter component in my computed fields snippet above can be simplified to get its value for totalItems from the cart object rather than calculating it for itself.

You can check out the source code for my Shopping Cart app, which I use to demonstrate many of the concepts discussed in this article.

  • Chrome 76.0.3809.132
  • Vue.js 2.6.0
  • Vue.js devtools 5.1.1

Software developer walking the edge between legacy systems and modern technology. I also make music: https://soundcloud.com/stephanbester