vueschool.io @vueschool_io
Daniel Kelly
Teacher @ Vue School
Full Stack developer (10 years)
Husband and Father
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
- Assign an id to the input
- Create an event listener to listen for the keyup event
- 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)
- 896