In this chapter, you will learn the key concepts of Vue.js and why you should consider Vue.js for your next project. You will learn how to run Vue projects from the command line; describe the Vue.js project architecture; create Vue single file components using various styling and HTML syntax flavors; and also write Vue methods and data objects and control HTML templates competently.
By the end of this first chapter, you will be able to describe the basics of Vue lifecycle hooks and expressions.
Developers in the industry are required to resolve frontend development problems quickly with minimal impact on existing workflows or backend architecture. In many cases, the UI is overlooked completely until the end of a project, which can happen because of a lack of resources, ever-evolving product requirements, or the existing attitude that the frontend is the easy bit. However, companies such as Apple and Google have proven that design thinking on the frontend is key to a solid product or platform that will excite and engage users leading to a higher return on investment and a more successful business.
If you have found Vue.js, you may have also come across other frontend frameworks that, at face value, solve the same problems such as Ember, Angular, or React. At the surface level, they all attempt to make reactive frontend development more reliable and introduce patterns that make this easier to do. However, there are some critical differences in how a Vue project might play out compared to an Angular or React project. Let's look into them.
Angular is a Model-View-ViewModel (MVVM) framework built by Google that, generally, enterprise companies in the past tended to favor because of Google's backing and the fact that from the ground up, Angular was created to be used alongside TypeScript. The ecosystem supporting Angular includes Ahead of Time (AoT) rendering, and router and CLI management, but fails to deliver a simplified global state management system; developers would need to learn and use Flux or adopt NgRx. Vue takes Angular's core ideas of robustness and reliability and improves the development experience with its agnostic approach to development by removing the restrictiveness of an enforced code style for developers. Simplifying familiar Angular patterns such as HTML directives and dependency injection for modularity with Vue's single-file component system benefits developers by removing the necessity to learn and remember a variety of structures (injectables, components, pipes, modules, and so on). Vue has excellent support for TypeScript and typing without the drawbacks that Angular can have with its enforced coding language and development style. React and Vue share a focus on component-driven development, which reduces the amount of time and effort needed to uptake a new framework.
The driving force behind React's popularity and large development community is attributed to Facebook's dedicated engineers and its 2013 open source release at a time when Angular 2+ was nowhere to be seen. React's JSX pattern (a way of writing HTML and CSS in JavaScript) introduces with it a heightened learning curve for new developers who are both required to learn another language and also wrap their heads around component-based architecture. Components allow developers to build applications in a modular way; individual components describe their own piece of functionality and lifecycle, where they can be instantiated when they are required and destroyed when they are not used. Vue takes these core concepts of modular coding and enables developers to build these components using either JSX or writing HTML, CSS, and JavaScript as you would a traditional web application in a single file. Vue's separation of concerns in a single-file component simplifies this modular structure for developers.
Vue has a gentle learning curve and a vibrant ecosystem. This benefits teams of any size by not requiring a huge amount of overhead to educate teams of developers on how to use the Vue.js framework.
For those developers who are looking to get off the ground quickly, do not reinvent the wheel by building a custom reactive pattern unless individual use cases require it. You can save time and money by using Vue as a framework because it is already performant and officially supports libraries that are necessary to build an end-to-end app, which include vue-router, Vuex state management, dev tools, and more.
In this chapter, we will start by introducing the Vue architecture before familiarizing you with Vue's unique SFC pattern and HTML template syntax sugar. You will learn how to work with the Vue-specific template syntax and coding patterns that include Vue bindings, directives, lifecycle hooks, scopes, and the local state. Out of the Vue ecosystem of official plugins, we will primarily be focusing on the core Vue libraries. First, let's look at Vue's project architecture.
One of the easiest ways to get started with Vue is to import the Vue package through a Content Distribution Network (CDN). By doing this you can create a Vue instance with the Vue function. Each Vue application consists of one root Vue instance that is created using the new Vue function. All corresponding Vue components that are created are also defined using the same syntax, however, are considered as nested Vue instances that can contain their own options and properties:
var vm = new Vue({
// options
})
Note
vm is a term commonly used to refer to a View Model, which is an abstraction of the view that describes the state of the data in the model. Binding a Vue instance to vm helps you to keep track of your Vue instance in a block of code.
In this example, we import Vue using the jsdelivr CDN, which will allow you to utilize the Vue functions:
<!DOCTYPE html>
<html>
<head>
<title>Vue.js CDN</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js">
</script>
</head>
</html>
Declare an element in the <body> tag using a class, ID, or data attribute. Vue is known for its ability to declaratively render data to the DOM using simple template syntax such as double curly braces to specify reactive content, for example, {{ text }}:
<!DOCTYPE html>
<html>
<head>
<title>Vue.js CDN</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js">
</script>
</head>
<body>
<div>
<p class="reactive-text">{{ text }}</p>
</div>
</body>
</html>
In the <head> tag, we see some vanilla JavaScript code that fires off when the DOM loads. This constructs a Vue component bound to the element with class .reactive-text. The data property labeled text will replace the curly brace placeholder with the string defined as Start using Vue.js today!:
<head>
<title>Vue.js CDN</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js">
</script>
<script>
document.addEventListener('DOMContentLoaded', function () {
new Vue({
el: '.reactive-text',
data: {
text: "Start using Vue.js today!"
}
})
})
</script>
</head>
<body>
<div>
<p class="reactive-text">{{ text }}</p>
</div>
</body>
</html>
In the preceding script, you bind the <p> element with the reactive-text class to the new Vue instance. So, now that Vue understands this HTML element you can use the {{ text }} syntax to output the data property text inside of the <p> element.
The output of the preceding code will be as follows:
Start using Vue.js today!
While a CDN is a very portable way to start including Vue.js in your projects, using package managers is the recommended installation method for Vue, which is compiled by webpack, because it allows you to control third-party library versions easily. You can access it here: https://vuejs.org/v2/guide/installation.html. We will explore what a webpack example looks like next.
Vue projects are structured similarly to a lot of modern node-based apps that contain a package.json file and a node_modules folder in the root of your project. Various other configuration files are usually contained at the root level, such as babel.config.js and .eslintrc.js, since they will generally have an effect across your whole project. The following screenshot displays a default Vue app folder structure:
The Vue project structure follows a pattern where most of your source code is managed within the /src directory. You can subdivide your Vue files into various folders, for example, using a components folder to store reusable Vue components. By default, Vue will create assets and a components folder to code split the default files. For beginners, it is good to follow this pattern until you get more comfortable with splitting up your code in ways that make sense for your application:
The public folder is a special directory for containing files that need to be transferred directly to the output location. The following screenshot displays how this folder will look:
By default, the public folder will contain an index.html file that serves as a placeholder for loading the Vue application. The index.html file can be modified to include header and footer scripts as required, such as Google Fonts or third-party JavaScript libraries that are not included as a part of your webpack bundle.
Components are the building blocks of most modern frameworks. Generally splitting your work into smaller chunks not only makes your code much easier to interpret but functionally follows the principles of Don't Repeat Yourself (DRY). One of the most unique patterns for Vue users with arguably one of the most benefits is the Single File Component (SFC) pattern. SFCs centralize the responsibility of both appearance and behavior into a single file, often simplifying the architecture of your project and making the development process simpler being able to refer to your HTML, CSS, and JavaScript logic without switching files. Your default .vue file structure will be as follows:
A trap that a lot of new Vue developers fall into is writing mega Vue files of over 500 lines of code, just for the HTML itself. Usually, what this means is that you could break this long component down into some smaller ones; however, we will cover file importing and code splitting in future chapters.
For example, in the header of your application, you may have a reusable logo element that needs to remain consistent on other pages. You would create a component such as logo.vue:
// logo.vue
<template>
<img src="myLogo.png" />
</template>
You can import it into your header component named header.vue:
// header.vue
<template>
<header>
<a href="mywebsite.com"><logo /></a>
</header>
</template>
<script>
import logo from 'components/logo.vue'
export default {
components: {
logo
}
}
</script>
Very soon, you will have lots of these semantically structured files, which use these small chunks of reusable syntax that your team can implement across various areas of your application.
In the next section, we will gain an understanding of data properties.
One of the most used terms and reactive elements used when constructing Vue components is data property. These manifest themselves within the data function of a Vue instance:
<template>
<div>{{color}}</div>
</template>
<script>
export default {
data() {
return {
color: 'red'
}
}
}
</script>
You can use data property to essentially store any information you want to use within your Vue templates. When this data property is updated or is changed, it will reactively update in the corresponding template.
In this exercise, we are going to build our first component inside of a Vue project. In this context, components are imported using ES6. We will require Node.js and yarn to be installed. These will be covered in the Preface. By the end of the exercise, you will be able to confidently create new Vue components using Vetur and import them into your project.
To access the code files for this exercise, refer to https://packt.live/35Lhycl.
> cd Exercise1.01/
> code .
> yarn
> yarn serve
Go to https://localhost:8080.
Note
Your app will hot reload when you save new changes, so you can see them instantly.
The following is the code generated after pressing Tab when using Vetur:
// src/App.vue
<template>
</template>
<script>
export default {
}
</script>
<style>
</style>
// src/components/Exercise1-01.vue
<template>
</template>
<script>
export default {
}
</script>
<style>
</style>
<template>
<div>
<h1>My first component!</h1>
</div>
</template>
<template>
<div>
<h1>My first component!</h1>
</div>
</template>
<style>
h1 {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
<template>
<Exercise />
</template>
<script>
import Exercise from './components/Exercise1-01'
export default {
components: {
Exercise,
}
}
</script>
When you press Save, https://localhost:8080 should reload and display the following output:
In this exercise, we saw how to structure Vue components using template tags, scaffold basic Vue components using Vetur, output HTML, and use ES6 syntax to import the Exercise1-01 component into App.vue.
Note
You can only have one root HTML element inside <template> tags. Complex components should be wrapped in a containing HTML tag of your choice. <div>, <article>, and <section> are all semantic HTML component wrappers.
Interpolation is the insertion of something of a different nature into something else. In the Vue.js context, this is where you would use mustache syntax (double curly braces) to define an area where you can inject data into a component's HTML template.
Consider the following example:
new Vue({
data() {
title: 'Vue.js'
},
template: '<span>Framework: {{ title }}</span>'
})
The data property title is bound to Vue.js reactive data and will update on the fly depending on state changes to the UI and its data. We will go into more depth about how to use interpolation and how to bind it to data properties in the next exercise.
When you want to output data into your template or make elements on a page be reactive, interpolate data into the template by using curly braces. Vue can understand and replace that placeholder with data.
To access the code files for this exercise, refer to https://packt.live/3feLsJ3.
> cd Exercise1.02/
> code .
> yarn
> yarn serve
Go to https://localhost:8080.
<script>
export default {
data() {
return {
title: 'My first component!',
}
},
}
</script>
<template>
<div>
<h1>{{ title }}</h1>
</div>
</template>
When you save this document, the data title will now appear inside your h1 tag.
<template>
<div>
<h1>{{ title.toUpperCase() }}</h1>
</div>
</template>
You should see an output like the following screenshot:
<template>
<div>
<h1>{{ isUppercase ? title.toUpperCase() : title }}</h1>
</div>
</template>
<script>
export default {
data() {
return {
title: 'My first component!',
isUppercase: false,
}
},
}
</script>
The preceding code will generate the following output:
<script>
export default {
data() {
return {
title: 'My first component!',
isUppercase: true,
}
},
}
</script>
The following screenshot displays the final output generated upon running the preceding code:
In this exercise, we were able to use inline conditionals inside the interpolated tags (curly braces) by using a Boolean variable. This allows us to modify what data is displayed inside of our component without overly complicated conditions, which can be useful in certain use cases.
We will now learn about how to style components using a variety of methods.
When using Vue components, the webpack compiler allows you to use almost any frontend templating language style you prefer. For example, there are several ways to compose CSS, either directly or with pre-processing. The easiest way to enable these expressive languages in your Vue templates is to install them when you set up your project ahead of time using the Vue CLI.
When using the style tag inside of a Vue component, you have the option to specify a language, provided you have installed the appropriate webpack loader. In Exercise 1.01, if you chose to install the SCSS preprocessor, you can add the lang="scss" attribute to the style tag to begin using SCSS.
For example, if you chose to install the Stylus preprocessor, you can add the lang="stylus" attribute to the style tag to begin using Stylus:
<style lang="stylus">
ul
color: #2c3e50;
> h2
color: #22cc33;
</style>
Vue scoping is a handy way to stop individual components from inheriting styles from the virtual DOM head. Add the scoped attribute to your style tag and write some component-specific styles that will override any other CSS rules from the global sheet. The general rule is to not scope global styles. A common method for defining global styling is to separate these styles into another style sheet and import them into your App.vue.
In this exercise, we will be utilizing the style tag to add SCSS preprocessed styles to a component and importing external stylesheets.
To access the code files for this exercise, refer to https://packt.live/3nBBZyl.
> cd Exercise1.03/
> code .
> yarn
> yarn serve
Go to https://localhost:8080.
// src/components/Exercise1-03.vue
<template>
<div>
<h1>{{ title }}</h1>
<h2>{{ subtitle }}</h2>
<ul>
<li>{{ items[0] }}</li>
<li>{{ items[1] }}</li>
<li>{{ items[2] }}</li>
</ul>
</div>
</template>
<script>
export default {
data() {
return {
title: 'My list component!',
subtitle: 'Vue JS basics',
items: ['Item 1', 'Item 2', 'Item 3']
}
},
}
</script>
<style lang="scss"></style>
src/styles/typography.scss
/* typography.scss */
$color-green: #4fc08d;
$color-grey: #2c3e50;
$color-blue: #003366;
h1 {
margin-top: 60px;
text-align: center;
color: $color-grey;
+ h2 {
text-align: center;
color: $color-green;
}
}
ul {
display: block;
margin: 0 auto;
max-width: 400px;
padding: 30px;
border: 1px solid rgba(0,0,0,0.25);
> li {
color: $color-grey;
margin-bottom: 4px;
}
}
Note
In SCSS, you can use standard CSS selectors to select elements in your component.
ul > li will select every <li> element inside of a <ul> element for styling. Similarly, using the addition symbol + means the elements placed after the first element will be styled if they match the condition. For example, h1 + h2 will dictate that all H2 elements after H1 will be styled in a way, but H3 will not. You can understand this better through the following example.
In CSS, you would present this code as follows:
h1 + h2 {
/* Add styling */
}
ul > li {
/* Add styling */
}
In SCSS, the same code can be represented as follows:
h1 {
+ h2 {
// Add styling
}
}
ul {
> li {
// Add styling
}
}
<style lang="scss">
@import '../styles/typography';
</style>
This will generate an output as follows:
<style lang="scss" scoped>
@import '../styles/typography';
h1 {
font-size: 50px;
color: $color-blue; // Use variables from imported stylesheets
}
</style>
The output of the preceding code is as follows:
Inspect the DOM and you will notice that at run-time, that scoping has applied v-data-* attributes to your DOM elements specifying these specific rules. Our typography.scss, which we are scoping to our component, references an HTML tag that does not live within the scope of our component. When Vue adds data attributes to the scoped component, it generates the style if the <body> tag exists within the component. In our case, it does not.
The Elements tab of your browser dev tools will show the following after expanding the <head> and <style> tags:
/* /src/styles/global.scss */
body {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
margin: 0;
}
<style lang="scss">
@import './styles/global';
</style>
Our app should now be back to normal, with a mixture of globally defined styling and properly scoped styles for this component, as follows:
In this exercise, we interpolated data that originated from an array, then styled our component using forms of scoped SCSS, which can both exist inside the <style> tag or be imported from another directory in our project.
A recent pattern that has become popular in the reactive framework world is CSS modules. Frontend development has always had to face the issue of conflicting CSS class names, ill-structured BEM code, and confusing CSS file structures. Vue components help to solve this by being modular and allowing you to compose CSS that, at compile time, will generate unique class names for the specific component that it was composed for. You can even have the exact same class names across components; however, they will be uniquely identified using a randomly generated string attached to the end.
To enable this feature in Vue, you will need to add the module attribute to the style block, and reference classes using JavaScript syntax:
<template>
<div :class="$style.container">CSS modules</div>
</template>
<style lang="scss" module>
.container {
Width: 100px;
Margin: 0 auto;
background: green;
}
</style>
In the preceding example, if you inspected the DOM tree that class will be called something like .container_ABC123. If you were to create multiple components that had a semantic class name like .container but used CSS modules, you would never run into style conflicts again.
In this exercise, you will utilize CSS modules to style a .vue component. By using the $style syntax inside of a :class bind, you refer to the Vue instance's this.$style scope. Vue will generate random class names based on the components at run or build time ensuring the style will not overlap with any other classes in your project.
To access the code files for this exercise, refer to https://packt.live/36PPYdd.
> cd Exercise1.04/
> code .
> yarn
> yarn serve
Go to https://localhost:8080.
<template>
<div>
<h1>{{ title }}</h1>
<h2>{{ subtitle }}</h2>
</div>
</template>
<script>
export default {
data() {
return {
title: 'CSS module component!',
subtitle: 'The fourth exercise',
}
},
}
</script>
<style lang="scss" module>
h1,
h2 {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
text-align: center;
}
.title {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
color: #2c3e50;
margin-top: 60px;
}
.subtitle {
color: #4fc08d;
font-style: italic;
}
</style>
<h1 :class="$style.title">{{ title }}</h1>
<h2 :class="$style.subtitle">{{ subtitle }}</h2>
When you save, your project should look something like this:
If you inspect the virtual DOM, you will see how it has applied unique class names to the bound elements:
In this exercise, we saw how to use CSS modules in your Vue components and how it works differently to CSS scoping. In the next exercise, we will learn how to write a template in PUG (HAML).
Note
In combination with file splitting and importing SCSS, CSS modules are the preferred method of scoping component styling here. This safely ensures that individual component styles and business rules do not risk overriding each other and do not pollute global styling and variables with component-specific styling requirements. Readability is important. The class name also hints to the component name as opposed to the v-data attribute, which can be good when debugging large projects.
With the right loader enabled you can use HTML abstractions such as PUG and HAML to template your Vue components instead of writing HTML.
To access the code files for this exercise, refer to https://packt.live/2IOrHvN.
> cd Exercise1.05/
> code .
> yarn
Go to https://localhost:8080.
vue add pug
yarn serve
<template lang="pug">
div
h1(class='title') {{ title }}
</template>
<script>
export default {
data() {
return {
title: 'PUG component!',
}
},
}
</script>
<style lang="scss">
.title {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
The preceding code will generate the following output:
In this exercise, we saw how to use other HTML languages for templating and to interpolate data in PUG format. After installing the Vue.js PUG plugin you can write your component syntax inside of these template tags using PUG by adding the lang attribute with the value pug.
Vue's templating language allows you to interpolate the HTML code with JavaScript expressions and Vue directives. This templating pattern is often referred to as syntax sugar because it does not change how the code itself works, just how you use it. Syntax sugar allows you to clearly define template-specific logic inside of your HTML without the need to abstract this logic elsewhere in your project or return copious amounts of HTML directly from your JavaScript code. All Vue based directives are prefixed with v-*, which indicates that it is a Vue specific attribute:
Note
<script> tags can be run in this directive. Only render content originating from secure or trusted sources.
Consider an example where we iterate over the list element five times. Each list item will render its count (1, 2… 5):
<ul><!-- do not apply v-for to this <ul> element -->
<li v-for="n in 5" :key="n">{{ n }}</li>
</ul>
Now let's look at how some of the basic directives work.
More complicated components will use multiple directives to achieve the desired outcome. In this exercise, we will construct a component that uses several directives to bind, manipulate, and output data to a template view.
To access the code files for this exercise, refer to https://packt.live/3fdCNqa.
> cd Exercise1.06/
> code .
> yarn
> yarn serve
Go to https://localhost:8080.
<template>
<div>
<h1>{{ text }}</h1>
</div>
</template>
<script>
export default {
data() {
return {
// v-text
text: 'Directive text',
}
},
}
</script>
<style lang="scss" scoped>
h2 {
margin: 40px 0 0;
font-weight: normal;
}
</style>
<template>
<div>
<h1 v-text="text">Loading...</h1>
</div>
</template>
<script>
export default {
data() {
return {
// v-text
text: 'Directive text',
}
},
}
</script>
Figure 1.17 displays the output of the preceding code:
<template>
<div>
<h1 v-once v-text="text">Loading...</h1>
</div>
</template>
<script>
export default {
data() {
return {
// v-text
text: 'Directive text',
}
},
}
</script>
...
<template>
<div>
<h1 v-once v-text="text">Loading...</h1>
<h2 v-html="html" />
</div>
</template>
<script>
export default {
data() {
return {
// v-text
text: 'Directive text',
// v-html
html: 'Stylise</br>HTML in<br/><b>your data</b>',
}
},
}
</script>
...
Running the preceding code will generate an output as follows:
<template>
<div>
<h1 v-once v-text="text">Loading...</h1>
<h2 v-html="html" />
</div>
</template>
<script>
export default {
data() {
return {
// v-text
text: 'Directive text',
// v-html
html: 'Stylise</br>HTML in<br/><b>your data</b>',
}
},
}
</script>
...
The following screenshot displays the output:
<template>
<div>
<h1 v-if="false" v-once v-text="text">Loading...</h1>
<h2 v-else-if="false" v-html="html" />
<a
v-else
:href="link.url"
:target="link.target"
:tabindex="link.tabindex"
v-text="link.title"
/>
</div>
</template>
You should only see the <a> tag in the page since we have set the conditional statements to false.
The v-else condition will display as follows:
<template>
<div>
<h1 v-show="true" v-once v-text="text">Loading...</h1>
<h2 v-show="false" v-html="html" />
<a
:href="link.url"
:target="link.target"
:tabindex="link.tabindex"
v-text="link.title"
/>
</div>
</template>
The output of the preceding code will be as follows:
When you open the Elements tab of your browser dev tools, you should be able to observe the h2 display state set to none, as follows:
If v-show results in a true Boolean, it will leave the DOM element as is. If it resolves as false, it will apply display: none styling to the element.
In this exercise, we learned about the core Vue directives to control, bind, show, and hide HTML template elements without requiring any JavaScript outside of adding new data objects to your local state.
In the next section, we will learn how to achieve a two-way binding with the help of Vue's v-model.
Vue has simplified the way to achieve two-way data binding by creating a directive that specifically watches a data property inside of your Vue component. The Vue directive v-model will reactively change when the bound data property that Vue is watching changes. This directive is usually useful for HTML form elements that need to both display the data and modify it reactively, for example, input, textarea, radio buttons, and so on.
Two-way binding is achieved by adding the v-model directive to the element you want bound and referring to a data prop:
<template>
<input v-model="name" />
</template>
<script>
export default {
data() {
return {
name: ''
}
}
}
</script>
Figure 1.23 represents the output generated by running the preceding code:
Be careful using this directive as binding a huge amount of data in this way can affect the performance of your application. Consider your UI and split these into different Vue components or views. Vue data in the local state is not immutable and can be redefined anywhere in the template.
We are going to build a component using Vue's two-way data binding attribute v-model. Consider what it means to bind a piece of data in two ways. The context for this form of data model is usually forms, or where you expect both input and output data. By the end of the exercise, we should be able to utilize the v-model attribute in a form context.
To access the code files for this exercise, refer to https://packt.live/2IILld8.
> cd Exercise1.07/
> code .
> yarn
> yarn serve
Go to https://localhost:8080.
<div class="form">
<label>
Name
<input type="text" v-model="name" />
</label>
</div>
<script>
export default {
data() {
return {
name: '',
}
},
}
</script>
<div class="form">
<label>
Name
<input type="text" v-model="name" />
</label>
<label>
Preferred javascript style
<select name="language" v-model="language">
<option value="Javascript">JavaScript</option>
<option value="TypeScript">TypeScript</option>
<option value="CoffeeScript">CoffeeScript</option>
<option value="Dart">Dart</option>
</select>
</label>
</div>
<script>
export default {
data() {
return {
name: '',
language: '',
}
},
}
</script>
Note
Wrap the form and the display area within another tag such as a <section> tag, as only one HTML element can be at the root of a template.
Your code should look as follows:
<template>
<section>
<div class="form">
<label>
Name
<input type="text" v-model="name" />
</label>
<label>
Preferred javascript style
<select name="language" v-model="language">
<option value="Javascript">JavaScript</option>
<option value="TypeScript">TypeScript</option>
<option value="CoffeeScript">CoffeeScript</option>
<option value="Dart">Dart</option>
</select>
</label>
</div>
<ul class="overview">
<li><strong>Overview</strong></li>
<li>Name: {{ name }}</li>
<li>Preference: {{ language }}</li>
</ul>
</section>
</template>
Exercise1-07.vue
37 <style lang="scss">
38 .form {
39 display: flex;
40 justify-content: space-evenly;
41 max-width: 800px;
42 padding: 40px 20px;
43 border-radius: 10px;
44 margin: 0 auto;
45 background: #ececec;
46 }
47
48 .overview {
49 display: flex;
50 flex-direction: column;
51 justify-content: space-evenly;
52 max-width: 300px;
53 margin: 40px auto;
54 padding: 40px 20px;
55 border-radius: 10px;
56 border: 1px solid #ececec;
57
58 > li {
59 list-style: none;
60 + li {
61 margin-top: 20px;
62 }
63 }
64 }
65 </style>
The complete code for this step is available at https://packt.live/36NiNXH.
Your output should look as follows:
Your form should look something like this. When you update the data in the form, it should also update the overview area synchronously.
In this exercise, we used the v-model directive to bind the name and JavaScript-style drop-down selection to our local state's data. When you change the data, it will reactively update the DOM elements we output this bound data to.
To loop over HTML elements in Vue, you utilize the v-for loop directive. When Vue renders the component, it will iterate the HTML element you have added the directive to in order to use the data being parsed into the directive. Anonymous loops can be performed using this directive, where you can define a number X and the loop will iterate that many times, which can be handy in situations where you can more strictly control how many loops you iterate on or for placeholder content. All loops require an iterator :key. When the key or the content bound to the key changes, Vue knows that it needs to reload the content inside the loop. If you have multiple loops in one component, randomize the key with extra characters or context-related strings to avoid :key duplication conflicts.
Anonymous loops are demonstrated below; note that you can use quotation marks or backticks (`) to describe strings:
<div v-for="n in 2" :key="'loop-1-' + n">
{{ n }}
</div>
<!-- Backticks -->
<div v-for="n in 5" :key="`loop-2-${n}`">
{{ n }}
</div>
The output of the preceding code should look as follows.
Understanding loops is key to not only working with Vue but also with JavaScript in general. Now that we have covered how to handle loops by using the v-for syntax and the importance of binding the :key property to add reactivity to the content being looped, we will utilize this function in the next exercise.
In this exercise, we are going to perform an anonymous loop using Vue's v-for directive. This will be familiar to those who have used for or foreach loops in JavaScript before.
To access the code files for this exercise, refer to https://packt.live/390SO1J.
Perform the following steps to complete the exercise:
> cd Exercise1.08/
> code .
> yarn
> yarn serve
Go to https://localhost:8080.
Exercise1-08.vue
1 <template>
2 <div>
3 <h1>Looping through arrays</h1>
4 <ul>
5 <li v-for="n in 5" :key="n">
6 {{ n }}
7 </li>
8 </ul>
The complete code for this step is available at https://packt.live/3pFAtgB.
This will generate an output as follows:
<template>
<div>
<h1>Looping through arrays</h1>
<ul>
<li v-for="(item, n) in interests" :key="n">
{{ item }}
</li>
</ul>
</div>
</template>
<script>
export default {
data() {
return {
interests: ['TV', 'Games', 'Sports'],
}
},
}
</script>
The following output is generated upon running the preceding code:
In this exercise, we learned how to iterate over both an arbitrary number and a specific array of strings, outputting the string value or index of an array. We also learned that the key attribute needs to be unique to avoid DOM conflicts and forces the DOM to re-render the component properly.
When requesting data from an API, you will often be iterating over an array of objects that contains both logic and raw content. Vue makes it easy to control the data's various states through its directive syntax. Conditional directives control the display state of DOM elements in Vue. The HTML syntax provides clear visibility when it comes to the display rules set in your component.
In this exercise, we will be controlling a Vue data array and iterating over the objects inside of it.
To access the code files for this exercise, refer to https://packt.live/32YokKa.
> cd Exercise1.09/
> code .
> yarn
> yarn serve
Go to https://localhost:8080.
<template>
<div>
<h1>Looping through array of objects</h1>
<ul>
<li v-for="(item, n) in interests" :key="n">
{{ item.title }}
</li>
</ul>
</div>
</template>
The output of the preceding code will be as follows:
<template>
<div>
<h1>Looping through array of objects</h1>
<ul>
<li v-for="(item, n) in interests" :key="n">
{{ item.title }}
<ol>
<li v-for="(fav, m) in item.favorite" :key="m"> {{ fav }}</li>
</ol>
</li>
</ul>
</div>
</template>
Figure 1.29 displays an output where looping is performed through an array of objects:
// src/components/Exercise1-09.vue
<template>
<div>
<h1>Looping through array of objects</h1>
<ul>
<li v-for="(item, n) in interests" :key="n">
{{ item.title }}
<ol v-if="item.favorite.length > 0">
<li v-for="(fav, m) in item.favorite" :key="m"> {{ fav }}</li>
</ol>
</li>
</ul>
</div>
</template>
This won't make a difference in the visuals of your page, but when you inspect the virtual DOM tree in your browser, you'll notice an HTML comment in dev mode allowing you to understand where a v-if statement might be false. When you build for production, these HTML comments won't be in your DOM.
By using the v-if directive in dev mode, you will see an HTML comment. These will not exist in production builds.
In this exercise we have been able to iterate over complex arrays of objects, outputting these objects' nested keys and controlling the view state of DOM elements based on length conditions.
Vue methods are defined inside the methods object within the Vue instance and can be written like normal JavaScript functions where you define a piece of logic that is executed. When you use JavaScript functions, normally, you would either return a value or simply perform a global action. The primary difference between writing functions and Vue methods is that the Vue method is scoped to your Vue component and can be run from anywhere inside the component it was written inside. Since the methods are scoped to your component's Vue instance, you can reference them inside of event directives easily in the HTML template. When binding events to HTML elements in Vue, you would use the @ symbol; for example, v-on:click is equivalent to @click.
In this exercise, we are going to build a component that uses Vue's methods API. Consider how similar these Vue methods can be written like your own named functions in JavaScript, as they behave in a very similar way. By the end of the exercise, we should be able to use methods and trigger them from the HTML template.
To access the code files for this exercise, refer to https://packt.live/3kMTWs5.
> cd Exercise1.10/
> code .
> yarn
> yarn serve
Go to https://localhost:8080.
<template>
<div>
<h1>Triggering Vue Methods</h1>
<ul>
<li v-for="n in 5" :key="n">
<a href="#">Trigger</a>
</li>
</ul>
</div>
</template>
<template>
<div>
<h1>Triggering Vue Methods</h1>
<ul>
<li v-for="n in 5" :key="n">
<a href="#" @click="triggerAlert(n)">Trigger {{ n }}</a>
</li>
</ul>
</div>
</template>
<script>
export default {
methods: {
triggerAlert(n) {
alert(`${n} has been clicked`)
},
},
}
</script>
Exercise1-10.vue
22 <style lang="scss" scoped>
23 ul {
24 padding-left: 0;
25 }
26 li {
27 display: block;
28 list-style: none;
29
30 + li {
31 margin-top: 10px;
32 }
33 }
34
35 a {
36 display: inline-block;
37 background: #4fc08d;
38 border-radius: 10px;
39 color: white;
40 padding: 10px 20px;
41 text-decoration: none;
42 }
43 </style>
The complete code for this step is available at https://packt.live/374yKZZ.
The following prompt is displayed when a trigger is clicked:
Note
While you can add an event directive to any HTML element, a suggestion would be applying them to native HTML interactive elements such as anchor tags, form input, or buttons to help with browser accessibility.
In this exercise, we were able to utilize the Vue methods API to define and trigger methods from the HTML template, and parse arguments into each method dynamically.
In this exercise, we are going to learn how to use Vue methods as a function to return data in the Vue instance and inside of the template.
Often in a web application, we want elements to appear on the page depending on whether a condition is met or not. For instance, if our product is not in stock, our page should display the fact that it is out of stock.
So, let's figure out how could we conditionally render these elements, depending on whether our product is in stock or not.
To access the code files for this exercise, refer to https://packt.live/3pHWCeh.
> cd Exercise1.11/
> code .
> yarn
> yarn serve
Go to https://localhost:8080.
<template>
<div>
<h1>Returning Methods</h1>
<div>Cart({{ totalItems }}) {{ totalCost }} </div>
<ul>
<li v-for="n in 5" :key="n">
<a href="#" @click="addToCart(n)">Add {{ n }}</a>
</li>
</ul>
</div>
</template>
<script>
export default {
data() {
return {
totalItems: 0,
totalCost: 0,
}
},
methods: {
addToCart(n) {
this.totalItems = this.totalItems + 1
this.totalCost = this.totalCost + n
},
},
}
</script>
<style lang="scss" scoped>
ul {
padding-left: 0;
}
li {
display: block;
list-style: none;
+ li {
margin-top: 10px;
}
}
a {
display: inline-block;
background: rgb(235, 50, 50);
border-radius: 10px;
color: white;
padding: 10px 20px;
text-decoration: none;
}
</style>
This will generate an output as follows:
When you click the buttons, the items counter should increment by 1, but the cost will increment by the n value, which should demonstrate normal cart functionality (clicking Add 2, then Add 5):
<template>
<div>
<h1>Returning Methods</h1>
<div>Cart({{ totalItems }}) {{ formatCurrency(totalCost) }} </div>
<ul>
<li v-for="n in 5" :key="n">
<a href="#" @click="addToCart(n)">Add {{ formatCurrency(n) }}</a>
</li>
</ul>
</div>
</template>
<script>
export default {
data() {
return {
totalItems: 0,
totalCost: 0,
}
},
methods: {
addToCart(n) {
this.totalItems = this.totalItems + 1
this.totalCost = this.totalCost + n
},
formatCurrency(val) {
return `$${val.toFixed(2)}`
},
},
}
</script>
The following screenshot displays the output of the preceding code:
In this exercise, we were able to utilize Vue's methods API to parse arguments into methods, return modified values, and use methods to update the local data state in a life-like scenario.
The Vue component lifecycle events include the following:
In this exercise, we will be learning how and when to use Vue's lifecycle hooks, and when they trigger by using JavaScript alerts. By the end of the exercise, we will be able to understand and use multiple Vue lifecycle hooks.
To access the code files for this exercise, refer to https://packt.live/36N42nT.
> cd Exercise1.12/
> code .
> yarn
> yarn serve
Go to https://localhost:8080.
Note
Feel free to swap the alert for console.log().
<template>
<div>
<h1>Vue Lifecycle hooks</h1>
<ul>
<li v-for="(item, n) in list" :key="n">
{{ item }}
</li>
</ul>
</div>
</template>
<script>
export default {
data() {
return {
list: [
'Apex Legends',
'A Plague Tale: Innocence',
'ART SQOOL',
'Baba Is You',
'Devil May Cry 5',
'The Division 2',
'Hypnospace Outlaw',
'Katana ZERO',
],
}
}
}
</script>
<script>
export default {
...
beforeCreate() {
alert('beforeCreate: data is static, thats it')
},
created() {
alert('created: data and events ready, but no DOM')
},
}
</script>
When you refresh your browser, you should see both alerts before you can see your list load on the page:
The following screenshot displays the created() hook alert after the beforeCreate() hook:
<script>
export default {
...
beforeMount() {
alert('beforeMount: $el not ready')
},
mounted() {
alert('mounted: DOM ready to use')
},
}
</script>
When you refresh your browser, you should also see these alerts before you can see your list load on the page:
The following screenshot displays the mounted() hook alert after the beforeMount() hook:
<template>
<div>
<h1>Vue Lifecycle hooks</h1>
<ul>
<li v-for="(item, n) in list" :key="n">
{{ item }} <a @click="deleteItem(item)">Delete</a>
</li>
</ul>
</div>
</template>
Exercise1-12.vue
17 <script>
18 export default {
19 data() {
20 return {
21 list: [
22 'Apex Legends',
23 'A Plague Tale: Innocence',
24 'ART SQOOL',
25 'Baba Is You',
26 'Devil May Cry 5',
27 'The Division 2',
28 'Hypnospace Outlaw',
29 'Katana ZERO',
30 ],
31 }
32 },
33 methods: {
34 deleteItem(value) {
35 this.list = this.list.filter(item => item !== value)
36 },
37 },
The complete code for this step is available at https://packt.live/3pJGLvO.
<style lang="scss" scoped>
ul {
padding-left: 0;
}
li {
display: block;
list-style: none;
+ li {
margin-top: 10px;
}
}
a {
display: inline-block;
background: rgb(235, 50, 50);
padding: 5px 10px;
border-radius: 10px;
font-size: 10px;
color: white;
text-transform: uppercase;
text-decoration: none;
}
</style>
<script>
export default {
...
beforeUpdate() {
alert('beforeUpdate: we know an update is about to happen, and have the data')
},
updated() {
alert('updated: virtual DOM will update after you click OK')
},
}
</script>
When you delete a list item by clicking the delete button in your browser, you should see these alerts.
<script>
export default {
...
beforeDestroy() {
alert('beforeDestroy: about to blow up this component')
},
destroyed() {
alert('destroyed: this component has been destroyed')
},
}
</script>
<script>
export default {
data() {
return {
list: [
'Apex Legends',
'A Plague Tale: Innocence',
'ART SQOOL',
'Baba Is You',
'Devil May Cry 5',
'The Division 2',
'Hypnospace Outlaw',
'Katana ZERO',
],
}
},
You should also see the destroy alerts after the update alerts are shown in your browser after you have saved this change with localhost running. This will generate the following output:
An alert will trigger every time you manipulate something on the page, demonstrating each available Vue lifecycle.
Note
Mounted and created lifecycle hooks will run every time a component loads. If this is not the desired effect, consider running the code you want to run once from the parent component or view, such as the App.vue file.
In this exercise, we learned what Vue lifecycle hooks are and when they trigger. This will be useful in combination with triggering methods and controlling data within your Vue components.
In this activity, we will build a dynamic shopping list app that will test your knowledge of Vue by using all the basic functions of an SFC, such as expressions, loops, two-way binding, and event handling.
This application should let users create and delete individual list items and clear the total list in one click.
The following steps will help you complete the activity:
The expected outcome is as follows:
Note
The solution for this activity can be found via this link.
In this chapter, you have learned how to run a Vue project using the command prompt and to create basic Vue components. Within these Vue components, you can scaffold template that use Vue's unique directives and HTML syntax sugar to loop over data or control DOM states with conditional statements. Key concepts of reactive data through the use of data props and the v-model binding were explored and made useful in real-life examples that utilized Vue.js methods and lifecycles.
In the next chapter, we will learn about more advanced reactive data concepts that will build upon this first chapter: using computed props and watchers and fetching asynchronous data from an external source.
18.191.181.36