DEV Community

Marcus Stamström
Marcus Stamström

Posted on

Coding with a Vue

Vue is this cool, popular and easy to learn UI framework. All you need to know to get started is HTML, CSS, and Javascript. So let's dig in and get started with learning how Vue works and building an application with VueJS. If you already know Vue and want to jump straight to the exercises, here is the link

An introduction

Vue is a progressive framework for building user interfaces. Progressive in this cases meaning that Vue both can exist in an existing solution as well as powering the whole website thanks to its rich ecosystem. Vue is focused on UI updates and leaves parts like routing and global state management out of the framework, but is easy to include if necessary.

When building an app in Vue, each page is split up into small reusable components that can be shared between components and pages.
https://vuejs.org/v2/guide/index.html#Composing-with-Components
When splitting up logic into smaller components, the code base becomes more manageable and also more testable. Each component has its own state, so if we would reuse a component in several places, changes to one component would not affect the others.

Vue utilizes a virtual DOM and can thereby decide when to best update the DOM and also perform asynchronous DOM updates. Which gives fast and optimized UI updates.

At the core of Vue.js, it's a system that enables us to declaratively render data to the DOM using straightforward template syntax. Consider the following example.

  <div id="app">
    {{ message }}
  </div>
  var app = new Vue({
    el: '#app',
    data: {
      message: 'Hello Vue'
    }
  })
  Hello Vue

Now we have created our first app, even though its a very simple app. Vue does, however, do a lot under the hood and have now linked the DOM with our component data. Hence when changing our data, the view will also change, making Vue components reactive.

HTML, CSS and Javascript

The only requirements for learning Vue is HTML, CSS, and Javascript. This is also what each component consists of, split up into different parts. Each Vue component consists of:

  • script tag, defining Javascript

  • template tag, defining HTML

  • style tag, defining CSS

For example, this is a component that prints Hello Vue!, and as we can see nothing more than HTML, CSS, and Javascript.

  <script>
    export default {
      name: 'app',
      data: () => ({ message: 'Hello Vue!' }),
    }
  </script>

  <template>
    <div class="app" >
      {{ message }}
    </div>
  </template>

  <style scoped>
    .app {
      color: greeen;
    }
  </style>

This is an exportable component that can be used by any other component. Note the <style scoped>, this is something that is included in Vue and makes the style scoped to the current component, leaving no other classes affected.

Vue instances

Every Vue application starts by creating a Vue instance with the function Vue

new Vue({
  render: h => h(App),
}).$mount('#app')

This usually looks something like this in the main.js file. Here we create our Vue instance and tell Vue that the root component is App, which is imported in main.js. Then Vue will create a component tree of the App component and all its subcomponents and so on. When Vue has created the component tree and calculated its initial state, it will insert the component tree on the #app element, usually a div element somewhere in the root HTML file.

Data

If we consider the root Vue instance, this instance needs to know when to perform an update on components in the component tree. This is where the data property comes in. The data property tells Vue which attributes that should trigger a rerender of that component.

How that works is that when Vue creates the component tree, it checks all the attributes in all the components data properties and creates getters and setters for each attribute. When one of these data attributes change, Vue will receive an event and can thereby trigger a rerender.

  data: function() {
    return { message: '' };
  }

So in the above example, when the message changes, Vue will trigger a rerender of this component. Attributes in the data property are accessed directly under this, so in this case, message could be altered with this.message

Methods

Methods are where we usually put logic regarding state changes of a component. Consider the following

  <script>
    export default {
      name: 'app',
      data: () => ({
        clicks: 0,
      }),
      methods: {
        onClick() {
          this.clicks = this.clicks + 1;
        },
      },
    }
  </script>

  <template>
    <div>
      <button @click="onClick">click me</button>
      <div>
        You clicked {{ clicks }} times!!
      </div>
    </div>
  </template>

This simple component counts each click. When we click, we call the onClick method that updates the clicks data attribute of this component. When the clicks data variable updates, Vue will notice and perform a rerender of this component, then displaying the correct value of the state of this component.

Template syntax

Vue uses an HTML like templating syntax, which is powerful and removes most of the need for writing Javascript in the template. In the template, we write HTML, with some additional Vue directives and declaratively bind the rendered DOM elements with the Vue instance data.

The most basic type of data binding is the double brackets, to print data to the DOM

  <div>
    {{ message }}
  </div>

Data binding

When we want to bind a certain piece of data to a component or element declaration in the template, we use the v-on directive.

  <h1 v-on:title="title">
    {{ message }}
  </h1>

v-on tells that title is a javascript element, which should be located in the script tag of the component. The v-on has a shorthand that is mostly used, :

  <h1 :title="title">
    {{ message }}
  </h1>

Events

When we want to listen to a DOM event, like click, we listen to this with the v-on vue directive as well. Vue has a different shorthand for events, @

  <button v-on:click="actionOnClick">
    click me
  </button>

  <!-- shorthand -->
  <button @click="actionOnClick">
    click me
  </button>

v-if vs v-show

v-if and v-show are 2 different ways of deciding if elements should be shown in the UI. They have a key difference in that v-if removes the element from the DOM when false, while v-show set display:none.

  <div v-if="show" />

  <div v-show="show" />

v-for

v-for is used when iterating over elements in the template. Keys "must" be given, since its the key the Vue binds to the DOM to the element. Keys must be unique for that element and providing a nonunique key will result in faulty updates.

  <div v-for="item in items" :key="item.id">
    {{ item.name }}
  </div>

Dont do this

  <div v-for="(item, index) in items" :key="index">
    {{ item.name }}
  </div>

Since the index is not specific for the item but for the iteration, if the elements in the items are would change place, like when sorting or filtering, wrong elements would update.

Component communication

A page in a Vue application is built up of many small components in a component tree as we saw in the components section. Quite often we want to communicate between components in the component tree. There are 2 ways of communication, up and down. When we communicate down we send data down to the child components, this will in the child component be visible as props. When a child component wants to communicate to the parent component they emit an event.

https://medium.com/@sky790312/about-vue-2-parent-to-child-props-af3b5bb59829

Let's also explain by example

  <script>
    export default {
      name: 'animals',
      data: () => ({
        animalList: ['dog', 'cat', 'horse']
        selectedAnimal: '',
      }),
      methods: {
        onSelect(animal) {
          this.selectedAnimal = animal;
        },
      },
    }
  </script>

  <template>
    <div>
      <dropdown :list="animalList" @selected="onSelect">
      <div v-if="selectedAnimal">
        You selected {{ selectedAnimal }}
      </div>
    </div>
  </template>

First, we have an animal component, this component displays a dropdown and the selected value of that dropdown. We send the list we want the dropdown to display to that component and we also listen for the event selected, for which we set the selectedAnimal data property.

  <script>
    export default {
      name: 'dropdown',
      props: ['list'],
      methods: {
        onSelect(event) {
          this.$emit('selected', event.target.value);
        }
      }
    }
  </script>

  <template>
    <select @change="onSelect">
      <option v-for="item in list" :value="item" :key="item">{{item}}</option>
    <select>
  </template>

The dropdown component renders the list given to it by props and emits an event when a value in the dropdown is selected. This shows how data flows down to child components by props and how events can be emitted and listened to by parent components.

Computed property

Computed are getters in a component. The result of the getters are cached and will only be recalculated if the values that they depend on in the data property change. Computeds can be used both in the script tag and in the template tag.

  computed: {
    getMessage() {
      return this.message.firstname + '' + this.message.lastname;
    }
  }

So first time this computed is used, the result will be cached and only be reevaluated if the message data attribute changes.

Computeds are also a good place to put Javascript code that would otherwise be placed in the template and something that the vue template directives don't cover. For example when we only want to iterate in the template over on part of an array.

  computed: {
    getFilteredArray() {
      return this.array.filter(item => item.message === this.message);
    }
  }

Then we could just filter over the getFilteredArray computed in the template instead of involving Javascript directly in the template.

Watch property

In the watch property, we can listen for changes in data, computed or props and when they change have a callback that triggers. Like

  watch: {
    message(value) {
      this.doStuff();
    }
  }

When message changes, in this case, we will call doStuff.

Lifecycle events

Each instance of a component in the component tree has a life span, in short:

  • its created when its inserted into the DOM

  • it gets updated during the time it's in the DOM if the props or data changes

  • it gets destroyed when it should be removed from the DOM.

In a Vue component, we can listen to these events and when they occur hook on to those events and perform actions. For example, one lifecycle event is called mounted, it's triggered when the component instance is mounted in the DOM. This event will happen one time during the life span of each component instance and when this event happens, we can decide what to do when our component has mounted. For example:

  mounted() {
    this.fetchFromApi().then(res => this.resultFromApi = res);
  }

The most commonly used lifecycle events are:

  • created, when the component is created and before it's inserted into the DOM

  • mounted, when the component is inserted into the DOM

  • updated, when the component will re-render

  • destroyed, when the component is destroyed

For a full example of when each lifecycle event is triggered, see the lifecycle diagram on vues docs.

Exercises

After reading this article, I hope you have got a good introduction to VueJS. I have created a series of exercises to try out VueJs and building a weather application. Please check out the exercises on my github.

Oldest comments (0)