vueschool.io         @vueschool_io

Daniel Kelly

Teacher @ Vue School

Full Stack developer (10 years)

Husband and Father

@danielkelly_io

@danielkellyio

Alabama, USA

Vue.js

Est. 2014

Evan You

Creator of Vue.js

Curious of how Angular worked

🤔

👷‍♂️

Tried to build it from scratch

Made something interesting

🤓

Vue.js version 0

was born

👼

Used by a ton of companies

🏋️‍♂️

a ton

+ Many more

Impressive growth

npmtrends.com/vue

Impressive growth

https://www.npmjs.com/package/vue

Developers are passionate about Vue

Vue is Easy to Use and Learn

Vue is Easy to Use and Learn

Vue.js

Vue is Easy to Use and Learn

React

Vue is Easy to Use and Learn

Vue is Good!

  • Lightweight (16kb min+gzip)
  • Supports tree-shaking
  • Performant
  • Scalable

Vue is progressive and easy to adopt

Can be used with any static page

Static HTML files

Can be used to build sophisticated apps

  • Single Page Applications

  • PWA's

  • Mobile apps

Vue.js Ecosystem

  • Vue Router
  • Pinia or Vuex
  • Vite or Vue CLI
  • Vue Devtools
  • Vue i18n
  • Vitest/Vue Test utils
  • Vue Server Renderer/Nuxt
  • and more!

Get Started with Vue.js

Vue can be used in any HTML page

Install Vue.js

<!-- hello.html -->
<html>
<body>
  <script src="https://unpkg.com/vue@3"></script>
</body>
</html>

Content Delivery Network

Install Vue.js

<!-- hello.html -->
<html>
<body>
  <script src="https://unpkg.com/vue@3"></script>
  <script>
    // we can use Vue in here
  </script>
</body>
</html>

Let's create a Vue Application Instance

The Vue Instance

<script>
  Vue.createApp({})
</script>

In order to use Vue in HTML we have to mount it to the DOM

Mount it to the DOM

<script src="https://unpkg.com/vue@next"></script>

<div id="app"></div>

<script>
  Vue.createApp({}).mount('#app')
</script>

Mount it to the DOM

⚡️ Vue Super Powers

<div id="app"></div>

<script>
  Vue.createApp({}).mount('#app')
</script>

Declarative Rendering

⚡️

A Vue Application

<div id="app">
  {{message}}
</div>

<script>
  Vue.createApp({
    data() {
      return {
        message: 'Hello Vueschool'
      }
    }
  }).mount('#app')
</script>

Options API

<script>
  Vue.createApp({
    data() {
      return {
        message: 'Hello Vueschool'
      }
    }
  }).mount('#app')
</script>

Data definition

Must be a function

Must return a data object

Reactivity System

⚡️

👱‍♀️User Input

A Vue Application

<div id="app">
  {{message}}
  <input v-model="message">
</div>

<script>
  Vue.createApp({
    data() {
      return {
        message: 'Hello Vueschool'
      }
    }
  }).mount('#app')
</script>

v-model directive

Two-way data binding

👏

Let's try it together!

How would you do that with Vanilla JavaScript or jQuery?

Reactivity without Vue

  1. Assign an id to the input
  2. Create an event listener to listen for the keyup event
  3. Get the value from input and set it to the text you want to update

in every place you want to display the message

Vue.js Benefits

  • Faster development and prototyping
  • Fewer chances of bugs
  • Easier and faster to refactor

Questions?

Assignment # 1

👩‍💻👨🏽‍💻

⏰ 15 mins

Directives

⚡️

Directives

Special HTML attributes provided by Vue

Directives

Always start with a v-

<div id="app">
  {{message}}
  <input v-model="message">
</div>

Directives

Applies reactive behavior  to the DOM

v-bind allows us to bind Vue.js data to any HTML attribute

Common Directives

Common Directives: v-bind

<div id="app">
  <img v-bind:src="image.path" v-bind:alt="image.alt" />
</div>

<script>
  Vue.createApp({
    data() {
      return {
        image: {
          path: '/path/to/image.jpg',
          alt: 'Alternative text for an image'
        }
      }
    }
  }).mount('#app')
</script>
<div id="app">
    <img src="/path/to/image.jpg" alt="Alternative text for an image" />
</div>

Rendered Output

Common Directives: v-bind

<div id="app">
  <a v-bind:href="link.href">{{ link.text }}</a>
</div>

<script>
  Vue.createApp({
    data() {
      return {
        link: {
          href: 'https://amazon.com',
          text: 'Amazon'
        }
      }
    }
  }).mount('#app')
</script>
<div id="app">
    <a href="https://amazon.com">Amazon</a>
</div>

Rendered Output

v-bind shorthand :

<div id="app">
    <img v-bind:src="image.path" v-bind:alt="image.alt" />
  
    <img :src="image.path" :alt="image.alt" />
</div>

Conditional Rendering

v-if allows us to conditionally render elements

Common Directives

Common Directives: v-if

<div id="app">
  <p v-if="sunny">
    It's sunny today! 🌞
  </p>
</div>

<script>
  Vue.createApp({
    data() {
      return {
        sunny: true
      }
    }
  }).mount('#app')
</script>

v-else-if and v-else 

Common Directives

<div id="app">
  <p v-if="sunny">It's sunny today! 🌞</p>

  <p v-else>Is it raining? ☔️</p>
</div>

<script>
  Vue.createApp({
    data() {
      return {
        sunny: true
      }
    }
  }).mount('#app')
</script>

Common Directives: v-else

Common Directives: v-else-if

<div id="app">
  <p v-if="sunny">It's sunny today! 🌞</p>

  <p v-else-if="windy">It's windy today! 🌬</p>

  <p v-else>Is it raining? ☔️</p>
</div>

<script>
  Vue.createApp({
    data() {
      return {
        sunny: false,
        windy: true
      }
    }
  }).mount('#app')
</script>

Directives support inline expressions

<div id="app">
  <p v-if="weather === 'sunny'">It's sunny today! 🌞</p>

  <p v-else-if="weather === 'windy'">It's windy today! 🌬</p>

  <p v-else>Is it raining? ☔️</p>
</div>

<script>
  Vue.createApp({
    data() {
      return {
        weather: 'hurricane'
      }
    }
  }).mount('#app')
</script>

Directives: Inline expressions

Don't bloat your template with inline expressions

Keep it simple or extract the logic

Questions?

Assignment # 2

👩‍💻👨🏽‍💻

⏰ 15 mins

Break? (10 min)

☕️

List Rendering

<script>
  Vue.createApp({
    data() {
      return {
        users: [
          {id: 1, name: 'Bruce Wayne', alterEgo: 'Batman'},
          {id: 2, name: 'Clark Kent', alterEgo: 'Superman'},
          {id: 3, name: 'Tony Stark', alterEgo: 'Ironman'},
          {id: 4, name: 'Diana Prince', alterEgo: 'Wonder Woman'}
        ]
      }
    }
  }).mount('#app')
</script>

We often work with arrays

v-for allows us to render lists in the DOM

Common Directives

<div v-for="something in someArray">
  <!-- something is available here -->
</div>

Can be any name

Name of the data property

Will always be an item of the array

v-for syntax

<div id="app">
  <ul>
    <li v-for="food in favoriteFood">{{ food }}</li>
  </ul>
</div>


<script>
  Vue.createApp({
    data() {
      return {
        favoriteFood: [
          'Pizza',
          'Burger',
          'Hummus'
        ]
      }
    }
  }).mount('#app')
</script>

v-for example

<div>
  <ul>
    <li>Pizza</li>
    <li>Burger</li>
    <li>Hummus</li>
  </ul>
</div>

Rendered Output

<div v-for="user in users">
  {{ user.name }}
</div>

v-for scope

The item is available inside the element

v-for scope

The item is available inside child elements 

<div v-for="user in users">
  {{ user.name}}
  <span class="pull-right">{{ user.alterEgo }}</span>
</div>
<img 
  v-for="image in myImages" 
  :src="image.src"
  :alt="image.title" />

v-for scope

The item is available on the element

Assign a :key

When you want to reuse or reorder existing elements

So Vue can track each node’s identity

<div id="app">
  <table>
    <tr v-for="user in users" :key="user.id">
      <td>{{ user.id }}</td>
      <td>{{ user.name }}</td>
    </tr>
  </table>
</div>

<script>
  Vue.createApp({
    data() {
      return {
        users: [
          { id: 1, name: 'Huey' },
          { id: 2, name: 'Dewey' },
          { id: 3, name: 'Louie'},
        ]
      }
    }
  }).mount('#app')
</script>

:key example

  • Must be a unique identifier

  • Should not be the index of the given element in the array

v-for key

Questions?

Assignment # 3

👩‍💻👨🏽‍💻

⏰ 20 mins

👋

See you tomorrow

👋

Welcome Back!

Interactivity

Vue.js allows us to easily to attach event listeners in our HTML

v-on allows us to add event listeners to the DOM

Common Directives

<button v-on:click="raining = true">Make it rain!</button>

Event

Handler

v-on syntax

<button v-on:click="raining = true">Make it rain!</button>

Inline handler

v-on syntax

<button v-on:click="makeItRain">Make it rain!</button>

Extracted to a method

v-on syntax

Methods

<div id="app">
  <button v-on:click="makeItRain">Make it rain!</button>
</div>

<script>
  Vue.createApp({
    data() {
      return {
          raining: false
        }
      },
    methods: {
      makeItRain () {
        this.raining = true
      }
    }
  }).mount('#app')
</script>

Methods are defined on the Vue instance

  • In the template

  • Inside other methods

Methods can be used

<script>
  Vue.createApp({
    data() {
      return {
        raining: false
      }
    },
    methods: {
      makeItRain () {
        this.raining = true
      },
      makeItRainAndSayHi () {
        this.makeItRain()
        alert('How you doing?')
      }
    }
  }).mount('#app')
</script>

We can access the entire Vue application instance under this

<button @click="makeItRain">Make it rain!</button>
<button v-on:click="makeItRain">Make it rain!</button>

v-on shorthand @

Instead of

We can listen for all native HTML events with v-on

Common HTML events

  • focus

  • blur

  • change

  • scroll

  • drag/drop

  • click

  • submit

  • input

  • mouseover

  • keyup/keydown

+++

Event Modifiers

 A shortcut for altering how browser reacts to specific events

Event Modifiers

<form @submit.prevent="submitForm">
  ...
</form>

Event Modifier Example

submitForm(event) {
  event.preventDefault()
  // ...
}

Let's try it together!

By moving the modifier to the template we keep our methods clean

Source vuejs.org

<!-- the click event's propagation will be stopped -->
<a v-on:click.stop="doThis"></a>

<!-- the submit event will no longer reload the page -->
<form v-on:submit.prevent="onSubmit"></form>

<!-- modifiers can be chained -->
<a v-on:click.stop.prevent="doThat"></a>

<!-- just the modifier -->
<form v-on:submit.prevent></form>

<!-- only trigger handler if event.target is the element itself -->
<!-- i.e. not from a child element -->
<div v-on:click.self="doThat">...</div>

Available event modifiers

  • .stop
  • .prevent
  • .once
  • .passive
  • .capture
  • .self

Key Modifiers

Allows us to quickly attach listeners to specific keys

Key Modifiers

<textarea @keydown.esc="clearText" />

Key Modifier Example

Available key modifiers

  • .enter
  • .tab
  • .delete
    (captures both "Delete" and "Backspace" keys)
  • .esc
  • .space
  • .up
  • .down
  • .left
  • .right

Key Aliases

  • .ctrl
  • .alt
  • .shift
  • .meta

System Modifier Keys

  • .a
  • .b
  • ....

Any Valid Key Name

Let's try it together!

Questions?

Assignment # 4

👩‍💻👨🏽‍💻

⏰ 25 mins

Won't Do these slides in the vertical

⚠️

🔥

Let's do a review quiz

Directives

Clarifications and tips

Create custom directives

Tip #2

<!-- Dropdown example with multiple directives -->
<div v-click-outside="closeDropdown">
  
  <ul v-if="showDropdown" class="dropdown-menu" >
    <!-- dropdown items goes here -->    
  </ul>
  
  <button v-else @click="openDropdown">Menu</button>
  
</div>

Dropdown example

<div v-click-outside="closeDropdown">
  // ...
</div>

v-click-outside

A custom directive

<!-- Dropdown example with multiple directives -->
<div v-click-outside="closeDropdown">
  
  <ul v-if="showDropdown" class="dropdown-menu" >
    <!-- dropdown items goes here -->    
  </ul>
  
  <button v-else @click="openDropdown">Menu</button>
  
</div>

Directives syntax recap

Directive

Argument

Value

<!-- Dropdown example with multiple directives -->
<div v-click-outside="closeDropdown">
  
  <ul v-if="showDropdown" class="dropdown-menu" >
    <!-- dropdown items goes here -->    
  </ul>
  
  <button v-else @click="openDropdown">Menu</button>
  
</div>

Directives syntax recap

Directive
(no argument or value)

<!-- Dropdown example with multiple directives -->
<div v-click-outside="closeDropdown">
  
  <ul v-if="showDropdown" class="dropdown-menu" >
    <!-- dropdown items goes here -->    
  </ul>
  
  <button v-else @click="openDropdown">Menu</button>
  
</div>

Directives syntax recap

Directive
(no argument)

Value

Questions?

Break (10 min)

☕️

Vue Devtools

Vue Devtools

  • Gives us insight into the current state of our data
  • Let's us change values of data to see what happens

Vue Devtools

  • Google Chrome
  • Firefox
  • Standalone App

 

Vue Devtools

DEMO

Let's see how it works with the last exercise

Vue Devtools

Install it now 🙃

Vue Devtools

in Static files

Vue Devtools

in Static files

Vue Devtools

Keep it open while you work on Vue code 💪

Questions?

Computed Properties

Vue’s template syntax allows you to run any JavaScript code in your HTML

<div id="app">
  {{ message.split('').reverse().join('') }}
</div>

JavaScript in HTML

Let's try it together!

<div id="app">
  <h1>{{ message.split('').reverse().join('') }}</h1>
  <input v-model="message">
  <div>{{ message.split('').reverse().join('') }}</div>
  <span>{{ message.split('').reverse().join('') }} </span>
</div>

JavaScript in HTML

Not Easily Reusable

Extract this logic to Computed Properties

Extract logic to Computed Properties

<script>
  Vue.createApp({
    data() {
      return {
        message: 'To infinity and beyond'
      }
    },
    computed: {
      reversedMessage () {
        return this.message.split('').reverse().join('')
      }
    }
  }).mount('#app')
</script>

Extract logic to Computed Properties

<div id="app">
  {{ reversedMessage }}
</div>

Let's try it together!

Computed Properties

  • Act Like Getters
  • Eliminate Repetition
  • Re-Evaluate when their dependencies change

They are used to:

  • Perform Transformations and Computations

🔬Use Cases

Eliminate Repetition

<div id="app">
  <h1>{{ reversedMessage }}</h1>
  <input v-model="message">
  <div>{{ reversedMessage }}</div>
  <span>{{ reversedMessage }} </span>
</div>

<script>
  Vue.createApp({
    data() {
      return {
        message: 'To infinity and beyond'
      }
    },
    computed: {
      reversedMessage () {
        return this.message.split('').reverse().join('')
      }
    }
  }).mount('#app')
</script>

Act like getters

<div id="app">
  <p v-if="isSunny">It's sunny today!</p>
</div>

<script>
  Vue.createApp({
    data() {
      return {
        weather: 'sunny'
      }
    },
    computed: {
      isSunny () {
        return this.weather === 'sunny'
      }
    }
  }).mount('#app')
</script>

Act like getters

<div id="app">
  <p v-if="isSunny">It's sunny today!</p>
</div>

<script>
  Vue.createApp({
    data() {
      return {
        weather: 'sunny'
      }
    },
    computed: {
      isSunny () {
        return this.weather === 'sunny'
      }
    }
  }).mount('#app')
</script>

Filter Data

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

<script>
  Vue.createApp({
    data() {
      return {
        items: [
          {id: 1, name: 'Hat', quantity: 7, price: 498},
          {id: 2, name: 'Sock', quantity: 0, price: 59},
          {id: 3, name: 'Shoes', quantity: 4, price: 1189}
        ]
      }
    },
    computed: {
      itemsInStock() {
        return this.items.filter(item => item.quantity > 0)
      }
    }
  }).mount('#app')
</script>

Compute Data

<div id="app">
  <p>Cart total: {{ shoppingCartTotal }}</p>
</div>

<script>
  Vue.createApp({
    data() {
      return {
        cart: [
          {name: 'Hat', quantity: 1, price: 498},
          {name: 'Sock', quantity: 3, price: 59},
          {name: 'Shoes', quantity: 1, price: 1189}
        ]
      }
    },
    computed: {
      shoppingCartTotal() {
        return this.cart.map(item => item.price * item.quantity)
          .reduce((total, amount) => total + amount)
      }
    }
  }).mount('#app')
</script>

Compute Data

<div id="app">
  <p>Cart total: {{ shoppingCartTotal }}</p>
</div>

<script>
  Vue.createApp({
    data() {
      return {
        cart: [
          {name: 'Hat', quantity: 1, price: 498},
          {name: 'Sock', quantity: 3, price: 59},
          {name: 'Shoes', quantity: 1, price: 1189}
        ]
      }
    },
    computed: {
      shoppingCartTotal() {
        return this.cart.map(item => item.price * item.quantity)
          .reduce((total, amount) => total + amount)
      }
    }
  }).mount('#app')
</script>

Transform Data

<div id="app">
  <h1>Total is {{amountInDollars}}</h1>
</div>

<script>
  Vue.createApp({
    data() {
      return {
        amount: 344
      }
    },
    computed: {
      amountInDollars () {
        return `$${this.amount}.00`
      }
    }
  }).mount('#app')
</script>

Transform Data

<div id="app">
  <h1>Total is {{amountInDollars}}</h1>
</div>

<script>
  Vue.createApp({
    data() {
      return {
        amount: 344
      }
    },
    computed: {
      amountInDollars () {
        return `$${this.amount}.00`
      }
    }
  }).mount('#app')
</script>

Computed

VS

Methods

Computed

VS

Methods

  • Do not accept arguments
  • Never change the data
  • Should return a value
  • Re-evaluate based on dependencies
  • Accept arguments
  • Change the data
  • Returning a value is optional
  • Don't re-evaluate based on dependencies*
computed: {
  itemsInStock() {
    return this.items.filter(
      item => item.quantity > 0
    )
  }
}
methods: {
  setWeather (weather) {
    this.weather = weather
  }
}

Questions?

Assignment # 5

👩‍💻👨🏽‍💻

⏰ 25 mins

1. Vue 3 Fundamentals Workshop (⚠️Options API)

By Daniel Kelly

1. Vue 3 Fundamentals Workshop (⚠️Options API)

  • 546