Home Web Development Frontend Development Projects with Vue.js 3 - Second Edition

Frontend Development Projects with Vue.js 3 - Second Edition

By Maya Shavin , Raymond Camden , Clifford Gurney and 1 more
ai-assist-svg-icon Book + AI Assistant
eBook + AI Assistant $39.99 $27.98
Print $49.99 $29.98
Subscription $15.99 $10 p/m for three months
ai-assist-svg-icon NEW: AI Assistant (beta) Available with eBook, Print, and Subscription.
ai-assist-svg-icon NEW: AI Assistant (beta) Available with eBook, Print, and Subscription. $10 p/m for first 3 months. $15.99 p/m after that. Cancel Anytime! ai-assist-svg-icon NEW: AI Assistant (beta) Available with eBook, Print, and Subscription.
What do you get with a Packt Subscription?
Gain access to our AI Assistant (beta) for an exclusive selection of 500 books, available during your subscription period. Enjoy a personalized, interactive, and narrative experience to engage with the book content on a deeper level.
This book & 7000+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with a Packt Subscription?
This book & 6500+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with eBook + Subscription?
Download this book in EPUB and PDF formats, plus a monthly download credit
This book & 6500+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with a Packt Subscription?
Gain access to our AI Assistant (beta) for an exclusive selection of 500 books, available during your subscription period. Enjoy a personalized, interactive, and narrative experience to engage with the book content on a deeper level.
This book & 6500+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with eBook?
Along with your eBook purchase, enjoy AI Assistant (beta) access in our online reader for a personalized, interactive reading experience.
Download this book in EPUB and PDF formats
Access this title in our online reader
DRM FREE - Read whenever, wherever and however you want
Online reader with customised display settings for better reading experience
What do you get with video?
Download this video in MP4 format
Access this title in our online reader
DRM FREE - Watch whenever, wherever and however you want
Online reader with customised display settings for better learning experience
What do you get with video?
Stream this video
Access this title in our online reader
DRM FREE - Watch whenever, wherever and however you want
Online reader with customised display settings for better learning experience
What do you get with Audiobook?
Download a zip folder consisting of audio files (in MP3 Format) along with supplementary PDF
What do you get with Exam Trainer?
Flashcards, Mock exams, Exam Tips, Practice Questions
Access these resources with our interactive certification platform
Mobile compatible-Practice whenever, wherever, however you want
ai-assist-svg-icon NEW: AI Assistant (beta) Available with eBook, Print, and Subscription. ai-assist-svg-icon NEW: AI Assistant (beta) Available with eBook, Print, and Subscription. BUY NOW $10 p/m for first 3 months. $15.99 p/m after that. Cancel Anytime! ai-assist-svg-icon NEW: AI Assistant (beta) Available with eBook, Print, and Subscription.
eBook + AI Assistant $39.99 $27.98
Print $49.99 $29.98
Subscription $15.99 $10 p/m for three months
What do you get with a Packt Subscription?
Gain access to our AI Assistant (beta) for an exclusive selection of 500 books, available during your subscription period. Enjoy a personalized, interactive, and narrative experience to engage with the book content on a deeper level.
This book & 7000+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with a Packt Subscription?
This book & 6500+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with eBook + Subscription?
Download this book in EPUB and PDF formats, plus a monthly download credit
This book & 6500+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with a Packt Subscription?
Gain access to our AI Assistant (beta) for an exclusive selection of 500 books, available during your subscription period. Enjoy a personalized, interactive, and narrative experience to engage with the book content on a deeper level.
This book & 6500+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with eBook?
Along with your eBook purchase, enjoy AI Assistant (beta) access in our online reader for a personalized, interactive reading experience.
Download this book in EPUB and PDF formats
Access this title in our online reader
DRM FREE - Read whenever, wherever and however you want
Online reader with customised display settings for better reading experience
What do you get with video?
Download this video in MP4 format
Access this title in our online reader
DRM FREE - Watch whenever, wherever and however you want
Online reader with customised display settings for better learning experience
What do you get with video?
Stream this video
Access this title in our online reader
DRM FREE - Watch whenever, wherever and however you want
Online reader with customised display settings for better learning experience
What do you get with Audiobook?
Download a zip folder consisting of audio files (in MP3 Format) along with supplementary PDF
What do you get with Exam Trainer?
Flashcards, Mock exams, Exam Tips, Practice Questions
Access these resources with our interactive certification platform
Mobile compatible-Practice whenever, wherever, however you want
  1. Free Chapter
    Chapter 1: Starting Your First Vue Project
About this book
Are you looking to use Vue.js 3 for building web apps but don't know where to begin? Frontend Development Projects with Vue.js 3 will help you get to grips with the core concepts of this JavaScript framework using practical examples that simulate real-world web projects. With this updated edition, you’ll experience all aspects of the new and improved Vue.js 3 as you work on mini projects such as a chat interface, a shopping cart, a price calculator, a to-do app, and a profile card generator for storing contact details. These realistic projects are presented as bite-size exercises that you can enjoy even as you challenge yourself. Throughout the book, you'll discover how to manage data in Vue components, define communication interfaces between components, and handle static and dynamic routing to control application flow. You'll also work with Vite and Vue DevTools and learn how to handle transition and animation effects for an engaging user experience. Finally, you’ll see how to test your app and deploy it to the web. By the end of this Vue.js book, you'll have the skills that enable you to work like an experienced Vue developer to build professional apps that can be used by others and have the confidence to tackle real-world frontend web development problems.
Publication date:
March 2023
Publisher
Packt
Pages
628
ISBN
9781803234991

 

Starting Your First Vue Project

In this chapter, you will learn about the key concepts and benefits of Vue.js (Vue), how to set up the project architecture using the terminal (or command line), and how to create a simple Vue component with local data following the component fundamentals.

This chapter will cover the following topics:

  • Understanding Vue as a framework
  • Setting up a Vite-powered Vue application
  • Exploring data properties as a local state
  • Writing components with <script setup>
  • Understanding Vue directives
  • Enabling two-way binding using v-model
  • Understanding data iteration with v-for
  • Exploring methods
  • Understanding component lifecycle hooks
  • Styling components
  • Understanding CSS modules

By the end of this chapter, you will be able to describe the fundamentals of Vue lifecycle hooks and expressions and use various styling approaches and HTML syntax flavors to control the HTML template competently.

 

Technical requirements

The Node version has to be below v20 (preferable Yarn 1.22 and Node version above 16 and up to 19.x, and npm up to version 9.x.

The complete code for this chapter is available on GitHub at: https://github.com/PacktPublishing/Frontend-Development-Projects-with-Vue.js-3/tree/v2-edition/Chapter01

 

Understanding Vue as a framework

Developers in the industry must resolve frontend development problems quickly with minimal impact on existing workflows or backend architecture. In many cases, developers tend to overlook the UI 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 thinking through the design of 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 know Vue, you may have also come across other frontend frameworks that, at face value, solve the same problems, such as Ember, Angular, or React. At a surface level, they attempt to make reactive frontend development more reliable and introduce patterns that make it easier. However, there are significant differences in how a Vue project might play out compared to an Angular or React project. Let’s investigate them.

Angular versus Vue

Angular is a Model-View-ViewModel (MVVM) framework built by Google and has built-in support for TypeScript. The Angular ecosystem includes Ahead-of-Time (AoT) rendering, a router, and a CLI tool. However, it fails to deliver a simplified system for global state management; developers would need to learn how to use Flux or adopt NgRx.

Vue takes Angular’s core robustness and provides a better development experience by removing the restriction of an enforced code style for developers. Vue also simplifies common Angular patterns, such as HTML directives, and eliminates a variety of Angular’s project structures, such as injectables, components, pipes, modules, and so on. From Vue 3.0 onward, it provides excellent support for TypeScript and typing without the drawbacks of Angular-enforced coding styles.

Vue is more flexible, developer-friendly, efficient, and straightforward to set up and learn to use than Angular in many cases.

Next, let’s look at how Vue and React differ.

React versus Vue

First released in 2013 and backed by Meta (previously known as Facebook), React rapidly gained popularity in the developer community. React introduces the JSX pattern to write HTML syntax directly with JavaScript. With JSX, React increases the amount that new developers are required to learn about both JavaScript and component-based architecture.

Both React and Vue share the same component-driven development approach, allowing developers to build applications in a modular way. Each component contains its functionalities and lifecycle. Vue takes these core concepts of modular coding and offers flexibility to developers in choosing which approach to use to write their components: JSX or the traditional style, in which HTML, CSS, and JavaScript are separated.

Vue uses the Single-File Component (SFC) approach to leverage this modular structure into a single file while keeping the separation readable and understandable for developers.

Advantages of using Vue for your project

Vue has a gentler learning curve and a vibrant ecosystem. This gentle learning curve helps reduce overhead and cost for any team onboarding developers to a new Vue project.

One key benefit of Vue is its approachability for both new and veteran developers:

  • Out of the box, developers can use a well-optimized and performant framework on which to build scalable, dynamic frontend applications.
  • The SFC format pattern offers a modular and flexible blueprint that provides an enjoyable experience to developers. SFCs allow Vue to be genuinely versatile. You can implement basic functionalities and incrementally adopt pieces of a static site into Vue rather than overhaul your entire website.

As powerful as Redux and NgRx, Vuex (and lately Pinia) proves to be an outstanding official global state management tool that is flexible enough to meet most development needs.

Thanks to its stable performance; well-defined tools such as Vue Router, Pinia, Vuex, and so on; and a supportive community, developers can save time and money by choosing Vue for their development stack.

The following section explores the essential Vue architecture before deep-diving into the SFC pattern and template syntax.

Working with Vue

To learn about the Vue architecture, we will start by importing the Vue package into our coding playground. One straightforward way is to import the Vue package through the official Content Distribution Network (CDN). We can do so by creating an index.html file and adding a <script> tag to load the Vue CDN within the <head> section of the HTML template, as demonstrated in the following code block:

<!DOCTYPE html>
<html>
<head>
    <title>Vue.js project with CDN</title>
    <script src="https://unpkg.com/vue@3"></script>
</head>
</html>

The browser will also load the Vue package using the CDN defined in the script tag when loading the page. Once completed, you can utilize the Vue functions and start writing Vue code.

But first, let’s look at the Vue instance.

Understanding the Vue instance

In general, each Vue application consists of only one root Vue instance, which can be created using the Vue.createApp method:

const vm = Vue.createApp({
  // options
})

The Vue class constructor accepts an options object for the configurations and behavior of components. We call this approach Options API and we can use it for all corresponding Vue components. However, all of them are considered nested Vue instances, with their own options and properties.

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.

For the Vue engine to render the application instance, in our index.html file, we declare an <div> element within the <body> tag using a unique class name, ID, or data attribute as the main entry point for the application accordingly:

<body>
  <div id="vue-app"></div>
  <script>
    const vm = Vue.createApp({
    //Options
    })
  </script>
</body>

To render the Vue application in the browser, we need to trigger vm.mount() to mount the root component to the targeted HTML element using a unique selector. In this example, it is an id with a value of vue-app:

<body>
  <div id="vue-app"></div>
  <script>
    const vm = Vue.createApp({
               //Options
               })
    vm.mount('#vue-app')
  </script>
</body>

Now, you bind the <div> element with id="vue-app" to the new Vue instance.

Next, let’s define text with a value of "Start using Vue.js today!" and add it as a property of the return value for the data method in the application options:

const vm = Vue.createApp({
  data() {
   return {
    text: 'Start using Vue.js today!'
   }
  }
})

In the preceding code example, data is a function that returns an Object instance containing the local state (or local variables) of a component. We will discuss this further in an upcoming section of this chapter.

To render the content of text to the DOM, we use Vue template syntax, represented by double curly braces ({{}}) wrapped around the reactive content. In this case, we use {{ text }}, as shown in the following code:

<div id="vue-app">{{ text }}</div>

The Vue engine will replace the data property labeled text and the curly brace placeholder with the Start using Vue.js today! string.

The output of the preceding code will be as follows:

Figure 1.1 – Displaying “Start using Vue.js today!” using a local data property

Figure 1.1 – Displaying “Start using Vue.js today!” using a local data property

In the <head> tag, we can also use the DOM API to construct a Vue application instance and bound it to our target element (with the ID selector as #vue-app):

<head>
  <title>Vue.js CDN</title>
  <script src="https://unpkg.com/vue@3"></script>
  <script>
    document.addEventListener('DOMContentLoaded', function
    () {
     Vue.createApp({
       data(){
         return {
           text: "Start using Vue.js today!"
         }
       }
      }).mount('#vue-app')
     })
  </script>
</head>
<body>
 <div id="vue-app">{{text}}</div>
</body>

The output is the same for both approaches. However, we strongly recommend not using DOMContentLoaded.

While working with a CDN is very portable, we recommend using package managers as the installation method for Vue. From Vue 3 and above, Vue projects use Vite (or Vite.js) to initialize and bundle the code. You can access it here: https://vuejs.org/guide/quick-start.html#creating-a-vue-application.

Using a bundling management tool is very helpful for managing other third-party libraries and building an optimized code package for production. In the next section, we will explore a package-controlled example.

 

Setting up a Vite-powered Vue application

A Vue project is structured similarly to a lot of modern node-based apps and contains the following:

  • A package.json file
  • A node_modules folder in the root of your project
  • Various other configuration files are usually contained at the root level, such as vite.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:

Figure 1.2 – Default Vue application folder structure

Figure 1.2 – Default Vue application folder structure

By default, there is an index.html file at the root level that serves as a placeholder for loading the Vue application. You can modify this file to include header and footer scripts, such as Google Fonts or third-party JavaScript libraries that are not included as a part of your bundle.

The Vue project structure follows a pattern where you manage most of your source code 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, Vite will create assets and components folders to code-split the default files. For beginners, it is good to follow this pattern until you get more comfortable:

Figure 1.3 – Default Vue application src folder structure

Figure 1.3 – Default Vue application src folder structure

The public folder is a special directory containing files that need to be transferred directly to the output location. The following screenshot displays how this folder will look:

Figure 1.4 – Default Vue application public folder

Figure 1.4 – Default Vue application public folder

At this point, you should be somewhat familiar with how a Vue project structure looks. Next, we discuss Vue’s unique pattern – the SFC architecture.

Vue’s SFC architecture

Components are the building blocks of most modern frameworks. In general, splitting your code into component-specific chunks ensures code readability and facilitates the Don’t Repeat Yourself (DRY) principle. Vue’s SFC pattern follows this approach closely.

The SFC architecture centralizes the responsibility of both appearance and behavior into a single file, thus simplifying the architecture of your project. You now can refer to your HTML, CSS, and JavaScript logic without switching files. Your default .vue file structure will be as follows:

Figure 1.5 – Default .vue file structure

Figure 1.5 – Default .vue file structure

A general good practice is to ensure your components file doesn’t contain more than 500 lines of code. If you encounter this situation, it’s recommended to split them into smaller reusable components. For example, in the header of your application, you may have a logo element that is reused on other pages. You would create a component such as logo.vue:

// logo.vue
<template>
  <img src="myLogo.png" />
</template>

In header.vue, you import the logo component into the script section and then include it as a nested component of the header component. You can achieve this by declaring it as a property of the components field:

// header.vue
<script>
    import logo from 'components/logo.vue'
    export default {
        components: {
          logo
        }
    }
</script>

In the template section, you can use the logo as a normal HTML element, as shown here:

<template>
    <header>
      <a href="mywebsite.com">
        <logo />
      </a>
    </header>
</template>

The output will be a header with the logo image rendered – and you can reuse the logo component in any other component when needed.

Very soon, you will have lots of these semantically structured files, which use small chunks of a reusable syntax that your team can implement across various application areas.

In the next exercise, you will practice creating your first Vue component and displaying it in another component.

Exercise 1.01 – building your first component

We are going to build our first component, Exercise1.01, inside of a Vue project and import it to use it in the App.vue component using ES6 module syntax.

To access the code file for this exercise, refer to https://github.com/PacktPublishing/Front-End-Development-Projects-with-Vue.js/tree/v2-edition/Chapter01/Exercise1.01.

Note

Your app will hot-reload when you save new changes, so you can see them instantly.

To get started, execute the following steps:

  1. Use the application generated with npm init vue@3 as a starting point, or within the root folder of the code repository, navigate into the Chapter01/Exercise1.01 folder by using the following commands in order:
    > cd Chapter01/Exercise1.01/
    > yarn
  2. Run the application using the following command:
    yarn dev
  3. Go to https://localhost:3000.
  4. Open the exercise project in VS Code (by using the code . command within the project directory) or your preferred IDE.
  5. Open the src/App.vue file, delete everything in that file, and save.
  6. In your browser, everything should be a blank, clean state to start working from.
  7. The three primary components that make up a single-file component are the <template>, <script>, and <style> blocks. Add the following code blocks as our scaffolding for a Vue component:
    /** src/App.vue **/
    <template>
    </template>
    <script>
    export default {
    }
    </script>
    <style>
    </style>
  8. Create another file in the components folder called Exercise1-01.vue and repeat the same step for scaffolding the Vue component:
    // src/components/Exercise1-01.vue
    <template>
    </template>
    <script>
    export default {
    }
    </script>
    <style>
    </style>
  9. Within our Exercise1-01.vue component, compose a set of <div> tags, with an <h1> element and a heading inside the <template> tags:
    <template>
      <div>
        <h1>My first component!</h1>
      </div>
    </template>
  10. Inside the <style> block, add some styling as follows:
    <style>
      h1 {
        font-family: 'Avenir', Helvetica, Arial,
        sans-serif;
        text-align: center;
        color: #2c3e50;
        margin-top: 60px;
      }
    </style>
  11. Import our component into App.vue by using the ES6 import method and defining the component inside the components object in the <script> block. We can now reference this component inside the HTML by using its name in camelCase or kebab-case (both will work):
    <template>
      <Exercise />
    </template>
    <script>
    import Exercise from './components/Exercise1-01'
    export default {
      components: {
        Exercise,
      }
    }
    </script>

When you press Ctrl + S (or Cmd + S on macOS), https://localhost:3000 should reload and look amazing:

Figure 1.6 – localhost output for Exercise 1.01

Figure 1.6 – localhost output for Exercise 1.01

In this exercise, we saw how to structure Vue components using template tags, and scaffold basic Vue components using Vetur. We also created a new Vue component and reuse it in App.vue using ES6 syntax and property field components.

In the next section, we will gain an understanding of how to define the local state data of a component using data properties.

 

Exploring data properties as a local state

One of the most used terms and reactive elements used when constructing Vue components is data properties. These manifest themselves within the data() function of a Vue instance:

<script>
    export default {
        data() {
          return {
            color: 'red'
          }
        }
    }
</script>

You can use the data() function to create a local data object to essentially store any information you want to use within your Vue templates. This local object is bound to the component and we call it the local state data of the component. When any property of this local object is updated or changed, it will reactively update in the corresponding template.

Once we have defined our local data, we need to bind it to the template section to display its values in the UI, which is called data interpolation.

Interpolation is the insertion of something of a different nature into something else. In the Vue context, this is where you would use mustache syntax (double curly braces) to define an area in which you can inject data into a component’s HTML template.

Consider the following example:

<template>
  <span> {{ color }}</span>
</template >
<script>
export default {
  data() {
    return {
      color: 'red'
    }
  }
}
</script>

The data property of red is bound to Vue.js reactive data and will update during runtime, depending on state changes between the UI and its data.

At this point, we should look at how to define and bind local data in the most classical Vue way. With Vue 3.0, we enjoy a shorter and simpler approach to writing and importing components. Let’s explore it next.

 

Writing components with script setup

Starting from Vue 3.0, Vue introduces a new syntactic sugar setup attribute for the <script> tag. This attribute allows you to write code using Composition API (which we will discuss further in Chapter 5, The Composition API) in SFCs and shorten the amount of code needed for writing simple components.

The code block residing within the <script setup> tag will then be compiled into a render() function before being deployed to the browser, providing better runtime performance.

To start using this syntax, we take the following example code:

// header.vue
<script>
    import logo from 'components/logo.vue'
    export default {
        components: {
          logo
        }
    }
</script>

Then, we replace <script> with <script setup>, and remove all the code blocks of export default…. The example code now becomes as follows:

// header.vue
<script setup>
    import logo from 'components/logo.vue'
</script>

In <template>, we use logo as usual:

<template>
    <header>
      <a href="mywebsite.com">
        <logo />
      </a>
    </header>
</template>

To define and use local data, instead of using data(), we can declare regular variables as local data and functions as local methods for that component directly. For example, to declare and render a local data property of color, we use the following code:

<script setup>
const color = 'red';
</script>
<template>
  <div>{{color}}</div>
</template>

The preceding code outputs the same result as the example in the previous section –red.

As mentioned at the beginning of this section, <script setup> is the most useful when you need to use Composition API within SFCs. Still, we can always take advantage of its simplicity for simple components.

Note

From this point onward, we will combine both approaches and use <script setup> whenever possible.

In the following exercise, we will go into more detail about how to use interpolation and data.

Exercise 1.02 – interpolation with conditionals

When you want to output data into your template or make elements on a page reactive, interpolate data into the template by using curly braces. Vue can understand and replace that placeholder with data.

To access the code file for this exercise, refer to https://github.com/PacktPublishing/Frontend-Development-Projects-with-Vue.js-3/tree/v2-edition/Chapter01/Exercise1.02:

  1. Use the application generated with npm init vue@3 as a starting point, or within the root folder of the code repository, navigate into the Chapter01/Exercise1.02 folder by using the following commands in order:
    > cd Chapter01/Exercise1.02/
    > yarn
  2. Run the application using the following command:
    yarn dev
  3. Open the exercise project in VS Code (by using the code . command within the project directory) or your preferred IDE.
  4. Create a new Vue component file named Exercise1-02.vue in the src/components directory.
  5. Inside the Exercise1-02.vue component, let’s add data within the <script setup> tags by adding a function called data(), and return a key called title with your heading string as the value:
    <script>
    export default {
      data() {
        return {
          title: 'My first component!',
        }
      },
    }
    </script>
  6. Reference title by replacing your <h1> text with the interpolated value of {{ title }}:
    <template>
      <div>
        <h1>{{ title }}</h1>
      </div>
    </template>

When you save this document, the data title will now appear inside your h1 tag.

  1. In Vue, interpolation will resolve any JavaScript that’s inside curly braces. For example, you can transform the text inside your curly braces using the toUpperCase() method:
    <template>
      <div>
        <h1>{{ title.toUpperCase() }}</h1>
      </div>
    </template>
  2. Go to https://localhost:3000. You should see an output like the following screenshot:
Figure 1.7 – Display of an uppercase title

Figure 1.7 – Display of an uppercase title

  1. Interpolation can also handle conditional logic. Inside the data object, add a Boolean key-value pair, isUppercase: false:
    <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:

Figure 1.8 – Exercise 1.02 output after including the inline conditional statement

Figure 1.8 – Exercise 1.02 output after including the inline conditional statement

  1. Add this condition to the curly braces and when you save, you should see the title in sentence case. Play around with this value by changing isUppercase to true:
    <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:

Figure 1.9 – Displaying the uppercase title

Figure 1.9 – Displaying the uppercase title

  1. Now, let’s replace <script> with <script setup> and move all the local data declared within the data() function to its own variable names respectively, such as title and isUpperCase, as shown here:
    <script setup>
    const title ='My first component!';
    const isUppercase = true;
    </script>
  2. The output should remain the same as in Figure 1.9.

In this exercise, we were able to apply inline conditions within the interpolated tags ({{}}) by using a Boolean variable. The feature allows us to modify what data to display without overly complicated situations, which can be helpful in certain use cases. We also learned how to write a more concise version of the component using <script setup> in the end.

Since we are now familiar with using interpolation to bind local data, we will move on to our next topic – how to attach data and methods to HTML element events and attributes using Vue attributes.

 

Understanding Vue directives

All Vue-based directives start with a v-* prefix as a Vue-specific attribute:

  • v-text: The v-text directive has the same reactivity as with interpolation. Interpolation with {{ }} is more performant than the v-text directive. However, you may find yourself in situations where you have pre-rendered text from a server and want to override it once your Vue application has finished loading. For example, you can pre-define a static placeholder text while waiting for the Vue engine to eventually replace it with the dynamic value received from v-text, as shown in the following code block:
    <template>
      <div v-text="msg">My placeholder</div>
    </template>
    <script setup>
    const msg = "My message"
    </script>
  • v-once: When used, it indicates the starting point of static content. The Vue engine will render the component with this attribute and its children exactly once. It also ignores all data updates for this component or element after the initial render. This attribute is handy for scenarios with no reactivity needed for certain parts. You can combine v-once with v-text, interpolation, and any Vue directive.
  • V-html: Vue will parse the value passed to this directive and render your text data as a valid HTML code into the target element. We don’t recommend using this directive, especially on the client side, due to its performance impact and the potential security leak. The script tag can be embedded and triggered using this directive.
  • v-bind: This directive is one of the most popular Vue features. You can use this directive to enable one-way binding for a data variable or an expression to an HTML attribute, as shown in the following example:
    <template>
      <img v-bind:src="logo" />
    </template>
    <script setup>
    const logo = '../assets/logo.png';
    </script>

The preceding code demonstrates how to bind the logo data variable to image’s src. The img component now takes the source value from the logo variable and renders the image accordingly.

You can also use it to pass a local data variable as props to another component. A shorter way is using the :attr syntax instead of v-bind:attr. Take the preceding example, for instance. We can rewrite the template as follows:

<template>
  <img :src="logo" />
</template>
  • v-if: This is a powerful directive you can use to conditionally control how elements render inside a component. This directive operates like the if…else and if…else if… conditions. It comes with supporting directives, such as v-else, standing for the else case, and v-else-if, standing for the else if case. For example, we want to render different text when count is 2, 4, and 6. The following code will demonstrate how to do so:
    <template>
    <div v-if="count === 2">Two</div>
    <div v-else-if="count === 4">Four</div>
    <div v-else-if="count === 6">Six</div>
    <div v-else>Others</div>
    </template>
  • v-show: You can also control the visible state of HTML elements by using v-show. Unlike v-if, with v-show, the Vue engine still mounts the element to the DOM tree but hides it using the display: none CSS style. You can still see the content of the hidden element visible in the DOM tree upon inspecting it, but it is not visible on the UI to end users. This directive does not work with v-else or v-else-if. If v-show results in a true Boolean, it will leave the DOM element as is. If it resolves as false, it will apply the display: none style to the element.
  • v-for: We use the v-for directive to accomplish the goal of list rendering based on a data source. The data source is an iterative data collection, such as an array or object. We will dive deeper into different use cases for this directive in a separate section within this chapter.

We have gone over the most common directives in Vue. Let’s review and experiment with how to use these directives with the following exercise.

Exercise 1.03 – exploring basic directives (v-text, v-once, v-html, v-bind, v-if, v-show)

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 file for this exercise, refer to https://github.com/PacktPublishing/Frontend-Development-Projects-with-Vue.js-3/tree/v2-edition/Chapter01/Exercise1.03.

Let’s start the exercise by performing the following steps:

  1. Use the application generated with npm init vue@3 as a starting point, or within the root folder of the code repository, navigate into the Chapter01/Exercise1.03 folder by using the following commands in order:
    > cd Chapter01/Exercise1.03/
    > yarn
  2. Run the application using the following command:
    yarn dev
  3. Open the exercise project in VS Code (by using code . command within the project directory) or your preferred IDE.
  4. Create a new Vue component file named Exercise1-03.vue in the src/components directory.
  5. Inside Exercise1-03.vue, compose the following code to display the text content:
    <template>
      <div>
        <h1>{{ text }}</h1>
      </div>
    </template>
    <script setup>
    const text = 'Directive text';
    </script>
  6. Replace the {{}} interpolation with the v-text attribute. The output should not change:
    <template>
      <div>
        <h1 v-text="text">Loading...</h1>
      </div>
    </template>

Figure 1.10 displays the output of the preceding code:

Figure 1.10 – Same output for v-text and the interpolation method

Figure 1.10 – Same output for v-text and the interpolation method

  1. Add the v-once directive to the same element. This will force this DOM element to only load the v-text data once:
    <template>
      <div>
        <h1 v-once v-text="text">Loading...</h1>
      </div>
    </template>
  2. Underneath the h1 element, include a new h2 element that uses the v-html attribute. Add a new local data called html that contains a string with HTML formatting in it, as shown in the following code block:
    <template>
      <div>
        <h1 v-once v-text="text">Loading...</h1>
        <h2 v-html="html" />
      </div>
    </template>
    <script setup>
    const text = 'Directive text';
    const html = 'Stylise</br>HTML in<br/><b>your data</b>'
    </script>

Running the preceding code will generate an output as follows:

Figure 1.11 – Rendering HTML elements from a string using v-html

Figure 1.11 – Rendering HTML elements from a string using v-html

  1. Add a new local link object that contains a bunch of information such as the URL, target, title, and tab index. Inside the template, add a new anchor HTML element and bind the link object to the HTML element using the v-bind short syntax – for example, :href="link.url":
    <template>
      <div>
        <h1 v-once v-text="text">Loading...</h1>
        <h2 v-html="html" />
        <a
          :href="link.url"
          :target="link.target"
          :tabindex="link.tabindex"
          >{{ link.title }}</a>
      </div>
    </template>
    <script setup>
    const text = 'Directive text';
    const html = 'Stylise</br>HTML in<br/><b>your data</b>'
    const link = {
      title: "Go to Google",
      url: https://google.com,
      tabindex: 1,
      target: '_blank',
    };
    </script>

The following screenshot displays the output:

Figure 1.12 – Output on binding the reactive data from the Vue instance to any HTML attribute

Figure 1.12 – Output on binding the reactive data from the Vue instance to any HTML attribute

  1. Apply v-if="false" to the h1 element, v-else-if="false" to h2, and v-else to the a tag like this:
    <template>
      <div>
        <h1 v-if="false" v-once v-text="text">Loading...
        </h1>
        <h2 v-html="html" v-else-if="false" />
        <a
          v-else
          :href="link.url"
          :target="link.target"
          :tabindex="link.tabindex"
          >{{ link.title }}</a>
      </div>
    </template>

You should only see the <a> tag on the page since we have set the main conditional statements to false.

The v-else condition will display the following:

Figure 1.13 – false v-if statements hiding the whole HTML element from the DOM

Figure 1.13 – false v-if statements hiding the whole HTML element from the DOM

  1. Change the template to use v-show instead of the v-if statements, remove v-else from the <a> element, and change the value of v-show in h1 to true:
    <template>
      <div>
        <h1 v-show="true" v-once v-text="text">Loading...
        </h1>
        <h2 v-html="html" v-show="false" />
        <a
          :href="link.url"
          :target="link.target"
          :tabindex="link.tabindex"
          >{{ link.title }}</a>
      </div>
    </template>

The output of the preceding code will be as follows:

Figure 1.14 – Changing v-show to true will display the main directive text

Figure 1.14 – Changing v-show to true will display the main directive text

When you open the Elements tab of your browser Devtools, you should be able to observe that the h2 display state is set to none as follows:

Figure 1.15 – h2 has “display: none” for the false condition

Figure 1.15 – h2 has “display: none” for the false condition

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 two-way binding with the help of Vue’s v-model.

 

Enabling two-way binding using v-model

Vue achieves two-way data binding by creating a dedicated directive that watches a data property within your Vue component. The v-model directive triggers data updates when the target data property is modified on the UI. 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.

We can enable two-way binding by adding the v-model directive to the target element and binding it to our desired data props:

<template>
    <input v-model="name" />
</template>
<script>
      export default {
        data() {
          return {
            name: ''
          }
        }
      }
</script>

In Figure 1.16, the output generated by running the preceding code will be as follows:

Figure 1.16 – Output for the v-model example

Figure 1.16 – Output for the v-model example

Note

Binding a huge amount of data using v-model can affect the performance of your application. Consider your UI and split the data into different Vue components or views. Vue data in the local state is not immutable and can be redefined anywhere in the template.

In the next exercise, we are going to build a component using Vue’s two-way data binding and experiment with what it means to bind data in two ways.

Exercise 1.04 – experimenting with two-way binding using v-model

The context for this type of data model is usually forms or wherever you expect both input and output data. By the end of the exercise, we should be able to utilize the v-model attribute in the context of a form.

To access the code file for this exercise, refer to https://github.com/PacktPublishing/Frontend-Development-Projects-with-Vue.js-3/tree/v2-edition/Chapter01/Exercise1.04.

Let’s start the exercise by performing the following steps:

  1. Use the application generated with npm init vue@3 as a starting point, or within the root folder of the code repository, navigate into the Chapter01/Exercise 1.04 folder by using the following commands in order:
    > cd Chapter01/Exercise 1.04/
    > yarn
  2. Run the application using the following command:
    yarn dev
  3. Open the exercise project in your VS Code (by using the code . command within the project directory) or your preferred IDE.
  4. Create a new Vue component file named Exercise1-04.vue in the src/components directory.
  5. Inside Exercise1-04.vue, start by composing an HTML label and bind an input element to the name data prop using v-model inside the template area:
    <div class="form">
       <label>
         Name
         <input type="text" v-model="name" />
       </label>
    </div>
  6. Complete the binding of the text input by returning a reactive data prop called name in the <script> tag:
    <script>
    export default {
      data() {
        return {
          name: '',
        }
      },
    }
    </script>
  7. Next, compose a label and selectable HTML select tied to the language data prop using v-model inside of the template area:
        <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>
  8. Finish binding the select input by returning a reactive data prop called language in the <script> tag:
    <script>
    export default {
      data() {
        return {
          name: '',
          language: '',
        }
      },
    }
    </script>
  9. Below the form fields, output the name and language inside of an unordered list structure (<ul> and <li>) by using curly braces such as {{ name }}:

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>
  1. Add styling inside the <style> tag at the bottom of the component:
    <style>
    .form {
      display: flex;
      justify-content: space-evenly;
      max-width: 800px;
      padding: 40px 20px;
      border-radius: 10px;
      margin: 0 auto;
      background: #ececec;
     }
     .overview {
      display: flex;
      flex-direction: column;
      justify-content: space-evenly;
      max-width: 300px;
      margin: 40px auto;
      padding: 40px 20px;
      border-radius: 10px;
      border: 1px solid #ececec;
    }
    .overview > li {
      list-style: none;
    }
    .overview > li + li {
      margin-top: 20px;
    }
    </style>
  2. Go to https://localhost:3000. Your output should look as follows:
Figure 1.17 – Displaying the final form after the data is updated

Figure 1.17 – Displaying the final form after the data is updated

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 modify the data, it will reactively update the DOM elements to which we output its value.

Next, we will discuss our v-for directive further and different approaches to handling iterative data collection in Vue.

 

Understanding data iteration with v-for

To loop over HTML elements in Vue, you use the v-for loop directive directly on the target elements. When Vue renders the component, it will iterate the target to use and render the data being parsed into the directive, with the same concept as a normal JavaScript for loop.

Basic iteration using v-for

The basic syntax of v-for is as follows:

v-for="(item, index) in items" :key="index"

The preceding syntax example indicates that we are iterating through a list of items. We have access to a single item and its appearance index in the list in each iteration. :key is a required attribute, acting as the unique identifier of each iterating element rendered for the Vue engine to keep track.

When the key or item content changes, either programmatically or due to user interactions, the Vue engine triggers an update of the changed item on the UI. If you have multiple loops in one component, you should randomize the key attribute with extra characters or context-related strings to avoid key duplication conflicts.

There are various use cases for this direction. One straightforward use case is to perform anonymous loops, in which you can define a number, X, as a symbolic list, and the loop will iterate that X times. This can be handy in situations in which you strictly control the number of iterations you want or render some placeholder content.

In the following example, we see an anonymous loop in which the total iterations are 2 and we define key with a loop-1 prefix:

<template>
<div v-for="n in 2" :key="'loop-1-' + n">
    {{ n }}
</div>
<template>

You can also use template literals (with `` backticks) to compute strings without +:

<template>
<div v-for="n in 5" :key="`loop-2-${n}`">
    {{ n }}
</div>
<template>

The output of the preceding code in both approaches should look as follows

Figure 1.18 – Output of anonymous loops example

Figure 1.18 – Output of anonymous loops example

Now that we have covered how to handle basic loops by using v-for, we will utilize this function in the next exercise.

Exercise 1.05 – using v-for to iterate through an array of strings

In this exercise, we are going to create 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 file for this exercise, refer to https://github.com/PacktPublishing/Frontend-Development-Projects-with-Vue.js-3/tree/v2-edition/Chapter01/Exercise1.05.

Perform the following steps to complete the exercise:

  1. Use the application generated with npm init vue@3 as a starting point, or within the root folder of the code repository, navigate into the Chapter01/Exercise1.05 folder by using the following commands in order:
    > cd Chapter01/Exercise1.05/
    > yarn
  2. Run the application using the following command:
    yarn dev
  3. Open the exercise project in VS Code (by using code . command within the project directory) or your preferred IDE.
  4. Create a new Vue component file named Exercise1-05.vue in the src/components directory.
  5. Inside Exercise1-05.vue, we compose a new component with an <h1> element to render the static title of Looping through arrays, and an <ul> element containing an empty <li> tag:
    <template>
    <h1>Looping through arrays</h1>
    <ul>
      <li></li>
    </ul>
    </template>
  6. In the script section, let’s add a setup attribute to the script tag. Then, let’s declare an array of interests containing some strings as follows:
    <script setup>
    const interests = ['TV', 'Games', 'Sports']
    </script>
  7. Now, let’s go back to the template section and add the v-for directive on the <li> tag to iterate through interests. For each iteration, we get a combination of (item, index) from the interests, in which item outputs the string of the array, and index is the loop index. We map the key attribute to index, and display the value of item as shown in the following code block:
    <template>
    <h1>Looping through arrays</h1>
    <ul>
      <li v-for="(item, index) in interests"
         :key="index">{{ item }}</li>
    </ul>
    </template>
  8. Go to https://localhost:3000. The following output is as follows:
Figure 1.19 – Output of iterating through an array of strings

Figure 1.19 – Output of iterating through an array of strings

In this exercise, we learned how to iterate through 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.

Next, let’s experiment with iterating a collection of objects.

Iterating through an array of objects

In most practical scenarios, we work with data as objects, especially when iterating through an array of objects. Vue makes it easy to control various data states through its directive syntax. Like iterating through an array of strings, the directive syntax remains the same:

v-for="(item, index) in items" :key="index"

The item you receive is now an Object, with various properties. You can bind each property using what you have learned so far to display its value. For example, assume in item, we will have id, title, description, and another array, characteristics, containing some strings. We can display the title and description information for each item like so:

<template>
  <ul>
    <li v-for="(item, index) in items" :key="item.id">
      <h2>{{ item.title }}</h2>
      <span>{{ item.description }}</span>
    </li>
  </ul>
</template>

Note here we don’t use an index as the key; instead, we use id as the unique identifier for key. It is considered a more secure approach to use id or any other unique identifier and we also don’t need to include index in the syntax in this case since we don’t use it.

Since characteristics is an array, we display its values by using a v-for directive again for characteristics. You don’t have to use the same name, item, that the syntax example shows. Instead, you can give it a different name depending on how you want your variable to be.

In the following example, we use str for each element in the item.characteristics array:

<template>
  <ul>
    <li v-for="item in items" :key="item.id">
      <h2>{{ item.title }}</h2>
      <span>{{ item.description }}</span>
      <ul>
          <li v-for="(str, index) in item.characteristics"
           :key="index">
             <span>{{ str }}</span>
          </li>
      </ul>
    </li>
  </ul>
</template>

And in the script section, we define items as follows:

<script setup>
const items = [{
  id: 1,
  title: "Item 1",
  description: "About item 1",
  characteristics: ["Summer", "Winter", "Spring", "Autumn"]
}, {
  id: 2,
  title: 'Item 2",
  description: 'About item 2",
  characteristics: ["North", "West", "East", "South"]
}]
</script>

The preceding code will output as shown in Figure 1.20:

Figure 1.20 – Output of iterating through an array of object items

Figure 1.20 – Output of iterating through an array of object items

Understanding how to loop through collections of objects with v-for is essential and useful for handling data, especially with external data. In the next exercise, you will combine v-for and v-if to display a list of objects conditionally.

Exercise 1.06 – using v-for to iterate through an array of objects and using their properties in v-if conditions

In this exercise, we will be controlling a Vue data array and iterating through the objects inside of it.

To access the code file for this exercise, refer to https://github.com/PacktPublishing/Frontend-Development-Projects-with-Vue.js-3/tree/v2-edition/Chapter01/Exercise1.06.

Let’s start the exercise by performing the following steps:

  1. Use the application generated with npm init vue@3 as a starting point, or within the root folder of the code repository, navigate into the Chapter01/Exercise1.06 folder by using the following commands in order:
    > cd Chapter01/Exercise1.06/
    > yarn
  2. Run the application using the following command:
    yarn dev
  3. Open the exercise project in VS Code (by using the code . command within the project directory) or your preferred IDE.
  4. Create a new Vue component file named Exercise1-06.vue in the src/components directory.
  5. Inside Exercise1-06.vue, create an array of data objects, interests, as local data. Each interest contains a title string and a favorites array of strings:
    <script setup>
    const interests = [
            {
              title: "TV",
              favorites: ["Designated Survivor",
                          "Spongebob"],
            },
            {
              title: "Games",
              favorites: ["CS:GO"],
            },
            {
              title: "Sports",
              favorites: [],
            },
          ];
    </script>
  6. In template, we loop over interests and display the title for each item in the interests array:
    <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>
  7. Go to https://localhost:3000 and the output of the preceding code will be as follows:
Figure 1.21 – You should now see a list of titles in the browser

Figure 1.21 – You should now see a list of titles in the browser

  1. Let’s create a second v-for loop to iterate through a favorites list for each item. Note that we use different names – fav and m – for our nested loop:
    <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.favorites"
                :key="m">
                {{ fav }}</li>
            </ol>
          </li>
        </ul>
      </div>
    </template>

Figure 1.22 displays an output where looping is performed through an array of objects:

Figure 1.22 – Nested ordered list detailing your favorites

Figure 1.22 – Nested ordered list detailing your favorites

  1. When inspecting the DOM elements (press Ctrl + F12 or open Developer Tools), you can see there are some empty elements as in Figure 1.23. This is because the Vue engine still renders the <ol> element even though favorites is an empty array:
Figure 1.23 – Displaying empty DOM elements in your virtual DOM

Figure 1.23 – Displaying empty DOM elements in your virtual DOM

  1. Now, we need to hide that empty <ol> element after applying it. We will check whether the favorites array is empty (length > 0) and then display the ordered list HTML element. Let’s add a v-if directive to <ol> with the item.favorites.length > 0 condition:
    <ol v-if="item.favorites.length > 0">
      <li v-for="(fav, m) in item.favorites" :key="m">
        {{ fav }}
      </li>
    </ol>

This won’t make a difference to the visuals of your page, but when you inspect the DOM tree in your browser, you’ll notice an HTML comment in dev mode that allows you to understand where a v-if statement might be false. When you build for production, these HTML comments won’t be visible in your DOM tree:

Figure 1.24 – Output displaying no HTML comment in production builds

Figure 1.24 – Output displaying no HTML comment in production builds

In this exercise, we have iterated through complex arrays of objects, outputting the nested keys for these objects and controlling the view state of DOM elements based on length conditions.

Next, let’s experiment with iterating through a keyed collection (or Object).

Iterating through a keyed collection (Object)

We can generally use v-for for looping through any iterative data collection type. Object in JavaScript is a key-value data collection, and we can iterate through its properties using v-for.

The syntax example is like the previous syntax example for arrays of objects and strings, with a tiny difference. Here, we change the naming convention from (item, index) to (value, key), in which key is the object’s property, and value is that key property’s value. Vue also exposes one more parameter – index – to indicate that property’s appearance index in the target object. Thus, the syntax now becomes the following:

v-for="(value, key, index) in obj"

Here, obj is our target object to iterate.

For example, assume we have the following object named course, which contains a title, a description, and the name of the lecturer(s):

<script setup>
const course = {
  title: 'Frontend development with Vue',
  description: 'Learn the awesome of Vue',
  lecturer: 'Maya and Raymond'
}
</script>

In our template, we iterate through the course’s properties and output their value in the <index>.< key > : <value> format as shown in the following code block:

<template>
  <ul>
    <li v-for="(value, key, index) in course" :key="key">
      {{index}}. {{key}}: {{value}}
    </li>
  </ul>
</template>

The output will be as shown in Figure 1.25:

Figure 1.25 – Iterating and displaying values of the course’s properties

Figure 1.25 – Iterating and displaying values of the course’s properties

Looping through the properties of an object is also a joint development practice. It is the same concept as winding through any keyed collection type, such as a hash-map (mapping according to key), lookup dictionary (it is also an object), and so on. Since the syntax stays consistent between both array and object iteration, it helps reduce the need for refactoring or data conversion.

Next, you will practice how to write basic looping for Object properties.

Exercise 1.07 – using v-for to loop through the properties of Object

In this exercise, we will be controlling a Vue data object and iterating through the properties inside of it.

To access the code file for this exercise, refer to https://github.com/PacktPublishing/Frontend-Development-Projects-with-Vue.js-3/tree/v2-edition/Chapter01/Exercise1.07.

Let’s start the exercise by performing the following steps:

  1. Use the application generated with npm init vue@3 as a starting point, or within the root folder of the code repository, navigate into the Chapter01/Exercise1.07 folder by using the following commands in order:
    > cd Chapter01/Exercise1.07/
    > yarn
  2. Run the application using the following command:
    yarn dev
  3. Open the exercise project in VS Code (by using the code . command within the project directory) or your preferred IDE.
  4. Create a new Vue component file named Exercise1-07.vue in the src/components directory.
  5. Inside Exercise1-07.vue, let’s compose information for the local data within <script setup> as follows:
    <script setup>
    const information = {
          title: "My list component information",
          subtitle: "Vue JS basics",
          items: ["Looping", "Data", "Methods"],
        }
    </script>
  6. In the <template> section, we will loop through information and display the values of its properties:
    <template>
        <div>
          <div v-for="(value, key) in information"
            :key="key">
            {{key}}: {{ value }}
          </div>
        </div>
    </template>
  7. Go to https://localhost:3000 and the output will be as follows:
Figure 1.26 – Output using v-for on the information object

Figure 1.26 – Output using v-for on the information object

  1. Note that Vue renders the value for items, which is an array of strings, the same as how we declared using JavaScript. To render it in a better format, we use the built-in JavaScript toString() function to export all the elements’ values into a string with comma separation automatically:
    <template>
        <div>
          <div v-for="(value, key) in information"
            :key="key">
            {{key}}: {{ value.toString() }}
          </div>
        </div>
    </template>
  2. The final output will render the list as follows:
Figure 1.27 – Output using v-for and toString() on values

Figure 1.27 – Output using v-for and toString() on values

Understanding iterations (or 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 directive and the importance of the key property for proper reactivity enhancement, we will explore how to use, write, and trigger methods in a component.

 

Exploring methods

In Vue 2.0, Vue defines component methods inside the methods object as part of a Vue instance. You compose each component method as a normal JavaScript function. The Vue method is scoped to your Vue component and can be run from anywhere inside the component it belongs to. It also has access to the this instance, which indicates the instance of the component:

<script>
  export default {
    methods: {
        myMethod() { console.log('my first method'); }
    }
  }
</script>

From Vue 3.0 onward, with <script setup>, as with local data, you can define a method as a regular function and it will work the same way as with the traditional approach. Hence, we can rewrite the preceding code as follows:

<script setup>
  const myMethod = () => { console.log('my first method'); }
</script>

You then can bind the methods to HTML events of an element as its event listeners in the template section. When binding events to HTML elements in Vue, you would use the @ symbol. For example, v-on:click is equivalent to @click, as shown in the following code block:

<template>
    <button id="click-me" v-on:click="myMethod">Click me
    </button>
    <button id="click-me" @click="myMethod">Click me
      shorter</button>
</template>

Clicking on both buttons triggers the same myMethod() method and generates the same result.

Let’s build a component with some methods.

Exercise 1.08 – triggering methods

In this exercise, we are going to build a component that uses Vue’s methods API. Consider how similarly these Vue methods can be written to 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 file for this exercise, refer to https://github.com/PacktPublishing/Frontend-Development-Projects-with-Vue.js-3/tree/v2-edition/Chapter01/Exercise1.08

We will build a list of different elements. For each element, we bind an onClick event with a component method, and alert users about the index of the clicked element by performing the following:

  1. Use the application generated with npm init vue@3 as a starting point, or within the root folder of the code repository, navigate into the Chapter01/Exercise1.08 folder by using the following commands in order:
    > cd Chapter01/Exercise1.08/
    > yarn
  2. Run the application using the following command:
    yarn dev
  3. Open the exercise project in VS Code (by using the code . command within the project directory) or your preferred IDE.
  4. Create a new Vue component file named Exercise1-08.vue in the src/components directory.
  5. Inside Exercise1-08.vue, within the <script setup> section, let’s define a method, triggerAlert, that receives an index and displays an alert informing users which index has been clicked:
    <script setup>
    const triggerAlert = (index) => {
          alert(`${index} has been clicked`)
        }
    </script>
  6. In the template section, set up an anonymous v-for loop on an HTML list and add a button element inside the list element. Set the loop to iterate 5 times, and display the index value as each button’s label:
    <template>
      <div>
        <h1>Triggering Vue Methods</h1>
        <ul>
          <li v-for="index in 5":key="index">
            <button>Trigger {{index}}</button>
          </li>
        </ul>
      </div>
    </template>
  7. Add the @click directive, referencing the triggerAlert method, and pass the value of index as an argument:
    <template>
      <div>
        <h1>Triggering Vue Methods</h1>
        <ul>
          <li v-for="index in 5" :key="index">
            <button @click="triggerAlert(index)">Trigger
              {{ index }}</a>
          </li>
        </ul>
      </div>
    </template>
  8. Add a margin between each button for readability:
    <style>
    button {
      margin: 10px;
    }
    </style>
  9. Your page should feature a list of buttons that when clicked, trigger an alert with a message that contains the button number you clicked, as follows:
Figure 1.28 – Outputting a list of triggers

Figure 1.28 – Outputting a list of triggers

The following prompt is displayed when a trigger is clicked:

Figure 1.29 – Displaying a browser alert with the index number in it

Figure 1.29 – Displaying a browser alert with the index number in it

Note

While you can add an event listener to any HTML element, we suggest applying them to native HTML interactive elements such as anchor tags, form input, or buttons to help with browser accessibility.

At this point, you can utilize the Vue methods API to define and trigger methods from the HTML template, and parse arguments into each method dynamically. In the next exercise, we will explore how to return data with Vue methods within a Vue component.

Exercise 1.09 – returning data using Vue methods

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 we conditionally render these elements depending on whether our product is in stock or not.

To access the code file for this exercise, refer to https://github.com/PacktPublishing/Front-End-Development-Projects-with-Vue.js/tree/v2-edition/Chapter01/Exercise1.09.

We will build a list of different elements and demonstrate adding different quantities to a cart. Then, we will display the updated cart’s total value in a currency format by performing the following:

  1. Use the application generated with npm init vue@3 as a starting point, or within the root folder of the code repository, navigate into the Chapter01/Exercise1.09 folder by using the following commands in order:
    > cd Chapter01/Exercise1.09/
    > yarn
  2. Run the application using the following command:
    yarn dev
  3. Open the exercise project in your VS Code (by using the code . command within the project directory), or your preferred IDE.
  4. Create a new Vue component file named Exercise1-09.vue in the src/components directory.
  5. Inside Exercise1-09.vue, within the <script> section, we set up two data objects, totalItems and totalCost, which will be updated when a user clicks on our shop’s buttons:
    <script>
    export default {
      data(){
        return {
          totalCost: 0,
          totalItems: 0
        }
      }
    }
    </script>
  6. In the template section, we display the value of totalItems and totalCost accordingly:
    <template>
     <div>
       <h1>Returning Methods</h1>
       <div>Cart({{ totalItems }}) {{ totalCost }} </div>
     </div>
    </template>
  7. Within the script section, let’s create an addToCart method, which will update totalCost and totalItems for the current component based on the received number, n, by using this.totalCost and this.totalItems:
    <script>
    export default {
      data() {
        /*…*/
      },
      methods: {
        addToCart(n) {
          this.totalItems = this.totalItems + 1
          this.totalCost = this.totalCost + n
        },
      },
    }
    </script>
  8. Let’s iterate through a random amount to create buttons for adding a quantity to the cart. The quantity is the button’s index. Then, we bind the addToCart method to each button, with its index as the function’s input argument:
    <template>
      <div>
        <h1>Returning Methods</h1>
        <div>Cart({{ totalItems }}) {{ totalCost }} </div>
        <ul>
          <li v-for="n in 5" :key="n">
            <button @click="addToCart(n)">Add {{ n }}
              </button>
          </li>
        </ul>
      </div>
    </template>
  9. Add a 10px margin to the button element for readability:
    <style>
    button {
      margin: 10px;
    }
    </style>
  10. Go to https://localhost:3000 and the output is as follows:
Figure 1.30 – Pressing any of the buttons will demonstrate the cart logic

Figure 1.30 – Pressing any of the buttons will demonstrate the cart logic

When you click on the buttons, the totalItems counter should increment by 1, but totalCost will increment by the n value, which should demonstrate a normal cart functionality. For example, when clicking Add 2, then Add 5, the output will be as follows:

Figure 1.31 – Output displaying Returning Methods after increments of 2 and 5

Figure 1.31 – Output displaying Returning Methods after increments of 2 and 5

  1. Now, let’s format totalCost. Create a method called formatCurrency, which accepts one argument. We will return the same value after giving it two decimal points and a $ symbol:
    <script>
    export default {
      data() {
        /*…*/
      },
      methods: {
        addToCart(n) { /*…*/},
        formatCurrency(val) {
          return `$${val.toFixed(2)}`
        },
      },
    }
    </script>
  2. To use this method in the template, add it to the interpolated curly braces and pass the value that was there as an argument inside the method instead:
    <template>
      <div>
        <h1>Returning Methods</h1>
        <div>Cart({{ totalItems }}) {{
          formatCurrency(totalCost) }}
        </div>
        <ul>
          <li v-for="n in 5" :key="n">
            <button @click="addToCart(n)">Add {{
              formatCurrency(n) }}</button>
          </li>
        </ul>
      </div>
    </template>

The following screenshot displays the output of the preceding code:

Figure 1.32 – All the values now are in currency format while retaining the cart counter

Figure 1.32 – All the values now are in currency format while retaining the cart counter

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.

In the next section, we will explore a significant part of a component – the lifecycle and available component hooks in Vue.

 

Understanding component lifecycle hooks

The Vue component lifecycle events happen during a component’s lifecycle, from creation to deletion. They allow us to add callbacks and side effects at each stage of the component’s life when necessary.

Vue executes the events in order, as follows:

  • setup: This event runs before all other hooks, including beforeCreate. It doesn’t have access to this instance since the instance has not yet been created at this point. It is mainly for using Composition API and is treated in the same way Vue treats script setup. We will discuss this event more in Chapter 5, The Composition API.
  • beforeCreate: This runs when your component has been initialized. data has not been made reactive and events are not set up in your DOM.
  • created: You will be able to access reactive data and events, but the templates and DOM are not mounted or rendered. This hook is generally good to use when requesting asynchronous data from a server since you will more than likely want this information as early as possible before the virtual DOM is mounted.
  • beforeMount: A very uncommon hook, as it runs directly before the first render of your component and is not called Server-Side Rendering.
  • mounted: Mounting hooks are among the most common hooks you will use since they allow you to access your DOM elements so that non-Vue libraries can be integrated.
  • beforeUpdate: This runs immediately after a change to your component occurs and before it has been re-rendered. It’s useful for acquiring the state of reactive data before it has been rendered.
  • updated: It runs immediately after the beforeUpdate hook and re-renders your component with new data changes.
  • beforeUnMount: This is fired directly before unmounting your component instance. The component will still be functional until the unmounted hook is called, allowing you to stop event listeners and subscriptions to data to avoid memory leaks. Note this event is called beforeDestroy in Vue 2.x.
  • unmounted: All the virtual DOM elements and event listeners have been cleaned up from your Vue instance. This hook allows you to communicate that to anyone or any element that needs to know this has been done. This event in Vue 2.x is called destroyed.

Let’s do a small exercise to learn how and when to use Vue’s lifecycle hooks, and when they trigger.

Exercise 1.10 – using a Vue lifecycle to control data

In this exercise, we will be learning how and when to use Vue’s lifecycle hooks, and when they are triggered 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 file for this exercise, refer to https://github.com/PacktPublishing/Frontend-Development-Projects-with-Vue.js-3/tree/v2-edition/Chapter01/Exercise1.10.

We will build a list of different elements demonstrating adding different quantities to a cart. Then, we will display the updated cart’s total value in a currency format by performing the following:

  1. Use the application generated with npm init vue@3 as a starting point, or within the root folder of the code repository, navigate into the Chapter01/Exercise1.10 folder by using the following commands in order:
    > cd Chapter01/Exercise1.10/
    > yarn
  2. Run the application using the following command:
    yarn dev
  3. Open the exercise project in VS Code (by using the code . command within the project directory) or your preferred IDE.
  4. Create a new Vue component file named Exercise1-10.vue in the src/components directory.
  5. Inside Exercise1-10.vue, we start by creating an array of data to iterate through in a list element, set the key to n, and output the {{item}} value inside of the <li> element using curly braces:
    <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>
  6. Add beforeCreated() and created() as properties below the data() function. Set an alert or console log inside these hooks so that you can see when they are being triggered:
    <script>
    export default {
      data(){ /*…*/ },
      beforeCreate() {
        alert('beforeCreate: data is static, thats it')
      },
      created() {
        alert('created: data and events ready, but no
               DOM')
      },
    }
    </script>
  7. When you refresh your browser, you should see both alerts before you see your list load on the page:
Figure 1.33 – Observing the beforeCreate() hook alert first

Figure 1.33 – Observing the beforeCreate() hook alert first

  1. The following screenshot displays the created() hook alert after the beforeCreate() hook:
Figure 1.34 – Observing the before() hook alert after the beforeCreate() hook

Figure 1.34 – Observing the before() hook alert after the beforeCreate() hook

  1. Define beforeMount() and mounted() in the same way as in step 6. Set an alert or console log inside of these hooks so that you can see when they are being triggered:
    <script>
    export default {
      data() { /*…*/ },
      /*…*/
      beforeMount() {
        alert('beforeMount: $el not ready')
      },
      mounted() {
        alert('mounted: DOM ready to use')
      },
    }
    </script>
  2. When you refresh your browser, you should also see these alerts before you can see your list load on the page:
Figure 1.35 – Observing the beforeMount() hook alert after the create() hook

Figure 1.35 – Observing the beforeMount() hook alert after the create() hook

  1. The following screenshot displays the mounted() hook alert after the beforeMount() hook:
Figure 1.36 – Observing the mounted() hook alert after the beforeMount() hook

Figure 1.36 – Observing the mounted() hook alert after the beforeMount() hook

  1. Add a new button element inside your <li> element that renders the item output. Use a @click directive to bind this button to a method called deleteItem and pass the item value as an argument:
    <template>
      <div>
        <h1>Vue Lifecycle hooks</h1>
        <ul>
          <li v-for="(item, n) in list" :key="n">
            {{ item }}
            <button @click="deleteItem(item)">Delete</button>
          </li>
        </ul>
      </div>
    </template>
  2. Add a method called deleteItem into a methods object above your hooks but below the data() function. Inside this function, pass value as an argument and filter out items from the list array based on this value. Then, replace the existing list with the new list:
    <script>
    export default {
      data() { /*…*/ },
      /*…*/
      methods: {
        deleteItem(value) {
          this.list = this.list.filter(item => item !==
            value)
        },
      },
    }
    </script>
  3. Add beforeUpdate() and updated() as functions same as in step 9 and set an alert or console log inside them:
    <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 on the Delete button in your browser, you should see these alerts. For example, when deleting the first item in the list, beforeUpdated will trigger:

Figure 1.37 – BeforeCreated is called first after clicking on any delete button

Figure 1.37 – BeforeCreated is called first after clicking on any delete button

Then, updated triggers, as shown in the following screenshot:

Figure 1.38 – updated is called when the Vue engine finishes updating the component before rendering to the DOM

Figure 1.38 – updated is called when the Vue engine finishes updating the component before rendering to the DOM

  1. Continue adding beforeUnmount() and unmounted() to the component options as function properties. Set an alert or console log inside these hooks so that you can see when they are being triggered:
    <script>
    export default {
      /*...*/
      beforeUnmount() {
        alert('beforeUnmount: about to blow up this
          component')
      },
      unmounted() {
        alert('unmounted: this component has been
          destroyed')
      },
    }
    </script>
  2. Add a new string to your list array – for example, testing unmounted hooks:
    <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',
            'testing unmounted hooks',
          ],
        }
      },
  3. You should see the unmount alerts according to this order: beforeUnmountbeforeCreatedcreatedbeforeMountunmountedmounted. An example output screen displaying the alert for beforeUnmount is shown here:
Figure 1.39 – Alert displays when a component is about to be unmounted

Figure 1.39 – Alert displays when a component is about to be unmounted

Note

mounted and created lifecycle hooks will run every time a component is initialized. 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, when they trigger, and in what order they trigger. This will be useful in combination with triggering methods and controlling data within your Vue components.

Next, we will discuss how we style our Vue components using the <style> section.

 

Styling components

When using Vue components, the Vite compiler allows you to use almost any frontend templating language style. The easiest way to enable these expressive library plugins in your Vue templates is to install them when you initialize your project, or by using npm install (or yarn add) for the package.

When using the style tag inside of a Vue component, you can specify a language using the lang attribute, provided that you have installed that specific language plugin.

For example, if you chose to install the Stylus preprocessor, first you need to install the stylus package in your project as a dependency by performing the following command:

npm add -D stylus
#OR
yarn add -d stylus

Then, 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>

Another benefit of using Vue is scoping the style with the scoped attribute. This is a useful way to create isolated and component-specific CSS stylings. It also overrides any other CSS global rules, according to the CSS rule of specificity.

It is not recommended to 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 file.

Now, let’s practice importing SCSS, a pre-processor plugin for CSS, to use in your application, and write some scoped stylings with the following exercise.

Exercise 1.11 – importing SCSS into a scoped component

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 file for this exercise, refer to https://github.com/PacktPublishing/Frontend-Development-Projects-with-Vue.js-3/tree/v2-edition/Chapter01/Exercise1.11.

Let’s start by performing the following steps:

  1. Use the application generated with npm init vue@3 as a starting point, or within the root folder of the code repository, navigate into the Chapter01/Exercise1.11 folder by using the following commands in order:
    > cd Chapter01/Exercise1.11/
    > yarn
  2. Run the application using the following command:
    yarn dev
  3. Open the exercise project in VS Code (by using the code . command within the project directory) or your preferred IDE.
  4. Create a new Vue component file named Exercise1-11.vue in the src/components directory.
  5. Inside Exercise1-11.vue, let’s write some HTML that can be styled using SCSS. Let’s keep practicing the interpolation method:
    <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>
  6. Add the sass SCSS package as a project dependency:
    npm add -D sass
  7. Add the lang attribute to the style tag and add the scss value to enable SCSS syntax inside the style block:
    <style lang="scss"></style>
  8. Create a folder inside the src/ directory called styles. Inside this new folder, create a file called typography.scss:
    src/styles/typography.scss
  9. Inside typography.scss, add some styling for the template you composed in your component, such as defining color variables (green, grey, and blue) to reuse in different areas of related CSS rules, and some CSS styles for h1, h2, and the list elements:
    /* 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;
      }
    }

In SCSS, you can use standard CSS selectors to select elements in your component.

ul > li will select every <li> element inside of an <ul> element for styling. Similarly, using the addition symbol (+) means that 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 certain 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
  }
}
  1. In your component, import these styles by using the SCSS @import method:
    <style lang="scss">
    @import '../styles/typography.scss';
    </style>

This will generate an output as follows:

Figure 1.40 – When you save and reload, your project should have the style imported

Figure 1.40 – When you save and reload, your project should have the style imported

  1. Add the scoped attribute to your <style> tag to only apply these styles to this component instance. Use the variable from the $color-blue imported stylesheet:
    <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:

Figure 1.41 – The outcome of scoping styles

Figure 1.41 – The outcome of scoping styles

  1. Inspect the DOM and you will notice that at runtime, that scoping has applied v-data-* attributes to your DOM elements specifying these specific rules.

The Elements tab of your browser Devtools also shows the following after expanding the <head> and <style> tags:

Figure 1.42 – How the virtual DOM uses data attributes to assign scoped styles

Figure 1.42 – How the virtual DOM uses data attributes to assign scoped styles

  1. Create a new style sheet called global.scss in the styles folder, containing only stylings for the main body element:
    /* /src/styles/global.scss */
    body {
        font-family: 'Avenir', Helvetica, Arial,
          sans-serif;
        margin: 0;
    }
  2. Import this stylesheet into your App.vue:
    <style lang="scss">
    @import './styles/global.scss';
    </style>

Our app should render the same as before; only the font family for all elements should change to Avenir and there should be no margin for the main body, as follows:

Figure 1.43 – Properly scoped styles for Exercise 1.03

Figure 1.43 – Properly scoped styles for Exercise 1.03

In this exercise, we interpolated data that originated from an array and learned about some basic SCSS syntax. Then, we styled our component using forms of scoped SCSS, which can either exist inside the <style> tag or be imported from another directory into our project.

In the next section, we are going to experiment with how to write dynamic CSS for a component using Vue 3 features.

Setting up state-driven dynamic CSS in Vue 3

Vue 3.x introduces a new CSS function, v-bind(), to use within the style section of a Vue SFC. We use this function to create a one-way link between local data and a CSS value.

Under the hood, the Vue engine uses CSS custom properties (or CSS variables) to compute the dynamic stylings received from v-bind(). For each v-bind(), it generates a hashed custom property (with the -- prefix) and adds it to the component’s root element. All the custom properties are added as inline static styles and will be updated whenever the linked local data’s value changes.

For example, let’s have a component that prints out a title and contains a local data property, headingStyles. The headingStyles data object contains several fields such as marginTop, textAlign, and color, indicating the relevant CSS properties:

<template>
<h1>{{ title }}</h1>
</template>
<script>
export default {
  data() {
    return {
      title: 'Binding with v-bind example',
        headingStyles: {
          marginTop: '10px',
          textAlign: 'center',
          : '#4fc08d',
        }
    }
  }
}
</script>

At this point, the output does not have a custom style and will be as follows:

Figure 1.44 – Displaying the title without using v-bind() and custom CSS

Figure 1.44 – Displaying the title without using v-bind() and custom CSS

We now can bind headingStyles to the CSS stylings of h1 in the <style> section, by applying v-bind() accordingly:

<style>
h1 {
  margin-top: v-bind(headingStyles.marginTop);
  text-align: v-bind(headingStyles.textAlign);
  color: v-bind(headingStyles.color);
}
</style>

The output will now have custom CSS enabled:

Figure 1.45 – Output with v-bind() and custom CSS applied

Figure 1.45 – Output with v-bind() and custom CSS applied

If you open Developer Tools and inspect this h1 element in the Elements tab, you will see it has inline styles, as shown in Figure 1.47:

Figure 1.46 – Devtools inspection shows the inline styles with hashed custom properties generated

Figure 1.46 – Devtools inspection shows the inline styles with hashed custom properties generated

Since v-bind() is a Vue 3.x feature, it also supports local variables defined using script setup out of the box. You can re-write the code in the script setup standards, and the outputs stay the same.

v-bind() also support JavaScript expressions. To use JavaScript expressions, you need to wrap them in quotes. For example, we can take headingStyles from the previous example and re-define marginTop as a number only:

headingStyles: {
  marginTop: 10,
  textAlign: 'center',
  color: '#4fc08d',
}

In the <style> section, let’s compute margin-top for the h1 selector with the addition of 5px and add the px suffix:

<style>
h1 {
  margin-top: v-bind('`${headingStyles.marginTop + 5}px`');
  text-align: v-bind(headingStyles.textAlign);
  color: v-bind(headingStyles.color);
}
</style>

The output now has a margin top of 15px as shown in Figure 1.48:

Figure 1.47 – Generated custom property for margin-top is 15px

Figure 1.47 – Generated custom property for margin-top is 15px

Using v-bind() is very beneficial for defining theming dynamically and programmatically. However, it provides only one-way binding from the local data to the styling, not vice versa. In the next section, we will explore the opposite binding direction using CSS modules.

 

Understanding CSS modules

A recent pattern that has become popular in the reactive framework world is CSS modules. Frontend development always faces 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 will generate unique class names for the specific component at compile time.

Using CSS modules in Vue exports CSS styles from the style section into JavaScript modules and uses those styles in the template and logic computing.

To enable this feature in Vue, you will need to add the module attribute to the style block, and reference as classes using the :class and $style.<class name> syntax, as shown here:

<template>
    <div :class="$style.container">CSS modules</div>
</template>
<style module>
.container {
  width: 100px;
  margin: 0 auto;
  background: green;
}
</style>

Once you have enabled the CSS module, the Vue engine exposes the $style object containing all the defined selectors as objects for use within the template section, and this.$style to use within the component’s JavaScript logic. In the preceding example, you are binding the CSS stylings defined for the.container class selector to div using $style.container.

If you inspected the DOM tree, that class would be called something such as .container_ABC123. If you were to create multiple components that had a semantic class name such as .container but used CSS modules, you would never run into style conflicts again.

Now, let’s practice using CSS modules to style a Vue component.

Exercise 1.12 – styling Vue components using CSS modules

To access the code file for this exercise, refer to https://github.com/PacktPublishing/Frontend-Development-Projects-with-Vue.js-3/tree/v2-edition/Chapter01/Exercise1.12.

Let’s start by performing the following steps:

  1. Use the application generated with npm init vue@3 as a starting point, or within the root folder of the code repository, navigate into the Chapter01/Exercise1.12 folder by using the following commands in order:
    > cd Chapter01/Exercise1.12/
    > yarn
  2. Run the application using the following command:
    yarn dev
  3. Open the exercise project in VS Code (by using the code . command within the project directory) or your preferred IDE.
  4. Create a new Vue component file named Exercise1-12.vue in the src/components directory.
  5. Inside Exercise1-12.vue, compose the following code:
    <template>
      <div>
        <h1>{{ title }}</h1>
        <h2>{{ subtitle }}</h2>
      </div>
    </template>
    <script>
    export default {
      data() {
        return {
          title: 'CSS module component!',
          subtitle: 'The fourth exercise',
        }
      },
    }
    </script>
  6. Add the <style> block and add module as an attribute instead of scoped:
    <style 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>
  7. To use CSS modules in your template, you need to bind them to your HTML elements by using the :class syntax, which is the same as the v-bind:class directive:
    <h1 :class="$style.title">{{ title }}</h1>
    <h2 :class="$style.subtitle">{{ subtitle }}</h2>

When you save it, your project should look something like this:

Figure 1.48 – Output using CSS modules

Figure 1.48 – Output using CSS modules

  1. If you inspect the virtual DOM, you will see how it has applied unique class names to the bound elements:
Figure 1.49 – Generated CSS module class

Figure 1.49 – Generated CSS module class

In this exercise, we saw how to use CSS modules in your Vue components and how it works differently from CSS scoping.

In combination with file splitting and importing SCSS, using CSS modules is the preferred method for 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 at the component name as opposed to the v-data attribute, which can be good when debugging large projects.

In the next section, you will apply what you have learned in this chapter to build a dynamic shopping list app by combining directives, loops, two-way data, and method declaration for a Vue component together, with scoped CSS styling.

Activity 1.01 – building a dynamic shopping list app using Vue

To access the code file for this activity, refer to https://github.com/PacktPublishing/Frontend-Development-Projects-with-Vue.js-3/tree/v2-edition/Chapter01/Activity1.01

This activity aims to leverage your knowledge thus far about the basic features 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:

  1. Build an interactive form in one component using an input bound to v-model.
  2. Add one input field to which you can add shopping list items. Allow users to add items by using the Enter key by binding a method to the @keyup.enter event.
  3. Users can expect to clear the list by deleting all the items or removing them one at a time. To facilitate this, you can use a delete method, which can pass the array position as an argument, or simply overwrite the whole shopping list data prop with an empty array, [].

The expected outcome is as follows:

Figure 1.50 – Expected output of Activity 1.01

Figure 1.50 – Expected output of Activity 1.01

 

Summary

In this chapter, you learned how to create and run a Vue project using the command prompt with Vite. You also learned how to create basic Vue components. Within these Vue components, you can scaffold templates that use Vue’s unique directives and HTML syntax sugar to loop over data or control DOM states with conditional statements. The key concepts of reactive data using data props and the v-model binding were explored and demonstrated as useful through real-life examples that utilized Vue methods and lifecycles.e

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.sssssse

About the Authors
  • Maya Shavin

    Maya is Senior Software Engineer in Microsoft, working extensively with JavaScript and frontend frameworks and based in Israel. She holds a B.Sc in Computer Sciences, B.A in Business Management, and an International MBA from University of Bar-Ilan, Israel. She has worked with JavaScript and latest frontend frameworks such as React, Vue.js, etc to create scalable and performant front-end solutions at companies such as Cooladata and Cloudinary, and currently Microsoft. She founded and is currently the organizer of the VueJS Israel Meetup Community, helping to create a strong playground for Vue.js lovers and like-minded developers. Maya is also a published author, international speaker and an open-source library maintainer of frontend and web projects.

    Browse publications by this author
  • Raymond Camden

    Raymond Camden is a developer advocate for IBM. His work focuses on the MobileFirst platform, Bluemix, hybrid mobile development, Node.js, HTML5, and web standards in general. He is a published author and presents at conferences and user groups on a variety of topics. Raymond can be reached at his blog, on Twitter, or via email. He is the author of many development books, including Apache Cordova in Action and Client-Side Data Storage.

    Browse publications by this author
  • Clifford Gurney

    Clifford Gurney is a solution-focused and results-oriented technical lead at a series-A funded startup. A background in communication design and broad exposure to leading digital transformation initiatives enriches his delivery of conceptually designed front-end solutions using Vue JS. Cliff has presented at the Vue JS Melbourne meetups and collaborates with other like-minded individuals to deliver best in class digital experience platforms.

    Browse publications by this author
  • Hugo Di Francesco

    Hugo Di Francesco is a software engineer who has worked extensively with JavaScript. He holds a MEng degree in mathematical computation from University College London (UCL). He has used JavaScript across the stack to create scalable and performant platforms at companies such as Canon and Elsevier and in industries such as print on demand and mindfulness. He is currently tackling problems in the travel industry at Eurostar with Node.js, TypeScript, React, and Kubernetes while running the eponymous Code with Hugo website. Outside of work, he is an international fencer, in the pursuit of which he trains and competes across the globe.

    Browse publications by this author
Latest Reviews (2 reviews total)
I recommend this ebook because It´s a quite good reference for the Vue JS Framework.