Vue.js for Beginners #1

Vue is a frontend JavaScript framework that helps us quickly create user interfaces. It is more lightweight and beginner-friendly compared to other frameworks such as React or Angular. The core library of Vue focuses on the view layer only, which is the part that the users can see. That is also why the author named the framework Vue (pronounced as view).

Installation

To create a new Vue application, go to the terminal and run the following command:

1
npm init vue@latest

This command will prompt you with multiple options such as TypeScript, Vue Router, and Pinia. For this tutorial, we don’t need any of these features, but you can play around with them if you want.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
✔ Project name: … <your-project-name>
✔ Add TypeScript? … No / Yes
✔ Add JSX Support? … No / Yes
✔ Add Vue Router for Single Page Application development? … No / Yes
✔ Add Pinia for state management? … No / Yes
✔ Add Vitest for Unit testing? … No / Yes
✔ Add Cypress for both Unit and End-to-End testing? … No / Yes
✔ Add ESLint for code quality? … No / Yes
✔ Add Prettier for code formatting? … No / Yes

Scaffolding project in ./<your-project-name>...
Done.

Next, go to the project folder, install the necessary packages, and run the development server.

1
2
3
cd <your-project-name>
npm install
npm run dev

Open your browser and go to http://localhost:3000/, you should see Vue’s welcome page.

Vue welcome page

Introduction

Before we start, let’s take a look at what has been installed into our project folder.

Vue directory structure

There are a few things we are already familiar with. The node_modules contains the packages we installed. The public folder contains the files and resources that we wish to make public. The package-lock.json and package.json files are both for managing packages, which we talked about before in the JavaScript Basics tutorial, and the index.html file is the start point of our project.

For this tutorial, we’ll only focus on the files inside the src directory. The assets folder stores the images, CSS files and other resources. The main.js file mounts and configures all the Vue apps in our project, and it is also the script that we import into the index.html file.

The App.vue is the actual Vue app, this is where we do most of the coding. However, sometimes the app gets too big, it makes more sense if we divide the app into multiple components, we’ll store these components inside the components folder.

We’ll take a closer look at what happens when you go to http://localhost:3000/. Let’s start from index.html, and notice what we have inside the <body> tag.

1
2
3
<body>
  <div id="app"></div>
</body>

The only line of the code that matters is <div id="app"></div>. Why? Let’s go to main.js.

1
2
3
4
import { createApp } from "vue";
import App from "./App.vue";

createApp(App).mount("#app");

This file imports the Vue app, and mounts that app to the HTML element with id="app". Recall that # represents id and . represents class. This is why that <div> element is so important, even though it is empty.

Next, go to the App.vue file:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
<script setup>
import HelloWorld from "./components/HelloWorld.vue";
</script>

<template>
  <img alt="Vue logo" src="./assets/logo.png" />
  <HelloWorld msg="Hello Vue 3 + Vite" />
</template>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  ...;
}
</style>

We immediately see that the file is divided into three sections. The <script> section contains the JavaScript code, the <template> contains HTML elements, and <style> contains CSS codes.

Notice in the <script> section, we imported a component from the components folder and used it in the <template> section.

And finally, go to the HelloWorld component. You can see that it has the exact same structure. You can also try to edit something inside this file and see if the webpage changes.

Basics

Now, let’s go back to the App.vue file, and delete everything unnecessary so we’ll have a clean and empty vue document.

1
2
3
4
5
<script></script>

<template></template>

<style></style>

Methods and properties

As you know, the <script> section is where we write JavaScript code, but since Vue is a framework, there are a few restrictions and requirements. This section usually has the following structure:

App.vue

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
<script>
export default {
  data() {
    return {
      name: "value"
    }
  },

  methods: {
    xxx() {...}
  },
  ...
}
</script>

This means when you are importing this Vue app into main.js, you are actually importing a bunch of methods and properties. Each property/method serves a different purpose.

For example, the data() method returns an object containing all the variables that are used in the app. Be careful that data must be a method and not just a property, this is what makes Vue reactive, meaning if the value of the variable changes, the web pages changes without having to reload. The methods property contains all the methods that are created by you, the coder. Of course, there are other properties allowed such as props, computed, inject and setup. We’ll discuss them in detail in the future.

A simple counter app

Knowing just these two simple concepts, the data method and the methods property, is enough for us to start creating apps. For example, we’ll create an app that counts how many times a button has been clicked.

App.vue

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
<script>
export default {
  data() {
    return {
      count: 0,
    };
  },
};
</script>

<template>
  <button v-on:click="count++">click me</button>
  <p>count = {{ count }}</p>
</template>

First, we declare a variable count, whose initial value is 0, and in the <template> section, we set up an event listener (v-on:click), each time the button is clicked, count increments by 1. The variable will then be rendered using double curly braces ({{ }}). We’ll talk about these syntaxes later.

What if we want another button that resets the value of count? This is what we can do:

App.vue

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
<script>
export default {
  data() {
    return {
      count: 0,
    };
  },

  methods: {
    clear() {
      this.count = 0;
    },
  },
};
</script>

<template>
  <button v-on:click="count++">click me</button>
  <button v-on:click="clear()">clear</button>
  <p>count = {{ count }}</p>
</template>

Remember to use the keyword this when referring to variables that belong to this application instance. The variable that we defined in the data method is unique to this instance, meaning it cannot be accessed by other instances or components. For example, we can create another counter, and import it into App.vue as a component.

components/Counter.vue

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<script>
export default {
  data() {
    return {
      count: 0,
    };
  },
  methods: {
    clear() {
      this.count = 0;
    },
  },
  components: { Counter },
};
</script>

<template>
  <button v-on:click="count++">click me</button>
  <button v-on:click="clear()">clear</button>
  <p>count = {{ count }}</p>
</template>

<style></style>

App.vue

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<script>
import Counter from "./components/Counter.vue";
export default {
  data() {
    return {
      count: 0,
    };
  },
  methods: {
    clear() {
      this.count = 0;
    },
  },
  components: { Counter },
};
</script>

<template>
  <button v-on:click="count++">click me</button>
  <button v-on:click="clear()">clear</button>
  <p>count = {{ count }}</p>

  <Counter />
</template>

<style></style>

Counter

Try this in your own browser, and you will find that even though the variable we defined for Counter.vue and App.vue are both counter, they do not seem to affect each other, and when you reset the variable’s value, only the one in the same instance becomes 0.

Lifecycles

Finally, I’d like to introduce another important concept in Vue, it’s called lifecycles.

When an app instance is been created, it goes through a series of processes, such as initializing data, compiling the template, mounting the template onto the DOM, and updating the template as the data changes. This allows us to divide the life of an application instance into several stages, and Vue provides us with several lifecycle hooks that allow us to add our own code at different stages.

For example, the function created() allows us to add code that is supposed to run right after the instance has been created.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
<script>
export default {
  data() {
    return { count: 1 };
  },
  created() {
    console.log("initial count is: " + this.count);
  },
};
</script>

There are other lifecycle hooks that we could use. Here is a diagram showing all of them and where they are in the lifecycle.

lifecycle

Interpolations

In the first section of this article, we learned that a Vue file is divided into three sections, <template>, <script>, and <style>. However, we merely touched the surface of Vue.js last time, starting from now we are going to talk about the details of each of these sections, and we’ll start with the easiest, the template section.

We know that the template section only contains HTML codes, it shows what the Vue file will eventually be rendered into. However, it can’t be that simple, since we want the page to be reactive, we want it to change as the data changes. To do that, we need to inform Vue.js where to put the data.

Text

Text interpolation is the most basic form of data binding, which uses double curly braces like this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
<script>
export default {
  data() {
    return { msg: "This is a message." };
  },
};
</script>

<template>
  <p>Message: {{ msg }}</p>
</template>

Try to change the value of msg, and you’ll see that the page changes without having to be refreshed.

Raw HTML

However, what if we want the data to be more complex? Say we want to bind a piece of HTML code to a variable, see what happens when you try to output HTML with double curly braces:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
<script>
export default {
  data() {
    return { msg: '<span style="color: red">This is a message.</span>' };
  },
};
</script>

<template>
  <p>Message: {{ msg }}</p>
</template>

The data will be treated as plain text instead of HTML codes. To solve this problem, we need to tell Vue.js that the data we are trying to render is HTML, by using an HTML directive:

1
<p>Message: <span v-html="msg"></span></p>

This time, when the data is being rendered, the original <span> tag will be replaced.

Attributes

Sometimes it might be useful if we bind an attribute to a variable. For instance, we want to enable a button when the user is verified, and disable it when the user is not verified. We can bind the disabled attribute to the verified variable by using the v-bind directive.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
<script>
export default {
  data() {
    return { verified: false };
  },
};
</script>

<template>
  <button v-bind:disabled="!verified">Button</button>
</template>

Remember that the exclamation mark (!) inverts the value of varified.

JavaScript expressions

It is also possible for us to use simple JavaScript expressions inside the template. In fact, the !varified we just saw is a very simple example. We can also do something more complicated like these:

1
2
3
4
5
6
7
{{ number + 1 }}

{{ ok ? "YES" : "NO" }}

{{ message.split("").reverse().join("") }}

<div v-bind:id="'list-' + id"></div>

However, there are some restrictions, for example, statements that declare new variables are not going to work. Loops and flow controls (if statements) are not going to work either.

Directives

In Vue.js, directives are special attributes with the prefix v-. Their primary function is to bind a side effect to a DOM node.

For instance, the following example binds a variable to the <p> element using the v-if directive. It works just like a regular if statement. When verified is true, the first <p> element will be rendered, and when verified is false, the second <p> element will be rendered.

1
2
<p v-if="verified">You are verified.</p>
<p v-if="!verified">You are NOT verified.</p>

Arguments

Some directives can take extra arguments. For example, the v-bind directive, which we’ve already seen, is used to bind an HTML attribute to a variable and it takes the name of that attribute as an argument.

1
2
<p v-bind:color="colorName">...</p>
<button v-bind:class="className">click me</button>

Another example is the v-on directive. It is the event listener in Vue.js.

1
<a v-on:click="action">...</a>

When this link is clicked, the function that is bonded to the variable action will be executed.

It is also possible to bind the argument itself to a variable. For example:

1
<a v-on:[event]="action">...</a>

In this case, if var event = "click", this example will be equivalent to v-on:click="action".

In fact, v-bind and v-on are the two most commonly used directives, that is why Vue.js has created special shortcuts for them. The v-bind can be shortened to just a colon (:), and v-on can be represented using just @.

The following codes are equivalent:

1
2
<a v-bind:href="url">...</a>
<a :href="url">...</a>
1
2
<a v-on:click="action">...</a>
<a @click="action">...</a>

Flow Control

Next, let’s talk about the if statements in Vue. Like we’ve seen before, the v-if directive binds the element with a boolean value. If the boolean value is true, the element will be rendered, and if it is false, the element will simply be ignored by Vue.

Other than v-if, there is also a v-else directive, which works with the v-if directive:

1
2
<p v-if="verified">You are verified.</p>
<p v-else>You are NOT verified.</p>

What if you need more than just two conditions? The v-else-if directive, as the name suggests, creates an else if block. It can be chained multiple times, hence creating multiple conditions.

1
2
3
4
5
<p v-if="num === 1">The number is 1.</p>
<p v-else-if="num === 2">The number is 2.</p>
<p v-else-if="num === 3">The number is 3.</p>
<p v-else-if="num === 4">The number is 4.</p>
<p v-else>The number is 5.</p>

Loops

Finally, other than if statements, Vue also allows us to create simple for loops inside the template. Its syntax actually resembles the for loops in Python, if you are familiar with the language.

We can render a list of items in an array like this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
<script>
export default {
  data() {
    return {
      items: [{ num: 1 }, { num: 2 }, { num: 3 }, { num: 4 }, { num: 5 }],
    };
  },
};
</script>

<template>
  <ul>
    <li v-for="item in items">The number is {{ item.num }}.</li>
  </ul>
</template>

Vue also supports an optional second argument for index number:

1
2
3
4
5
6
7
<template>
  <ul>
    <li v-for="(item, index) in items">
      #{{ index }} - The number is {{ item.num }}.
    </li>
  </ul>
</template>

comments powered by Disqus