Vue Fundamentals
I think of building web apps as putting LEGOs together.
Components are the building blocks in Vue.
We create them by combining HTML
, CSS
, & JS
.
A component must contain an opening & closing template
tag & template tags contain HTML
.
<template>
<h1>Wizards</h1> !!!!!
</template>
Templates contain 1 root tag(best practice).
<template>
<div>
<h1>Wizards</h1>
<ul>
<li>Harry</li>
<li>Ron</li>
<li>Hermione</li>
</ul>
</div>
</template>
Both standard & semantic HTML tags are available.
<template>
<div>
<h1>Wizards</h1>
<ul>
<li><b>Harry</b></li>
<li><i>Ron</i></li>
<li><strong>Hermione</strong></li>
</ul>
<input />
</div>
</template>
Standard HTML attributes such as style, class, autofocus, etc... apply.
<template>
<div>
<h1 style="color: red">Wizards</h1>
<ul>
<li>
<b style="font-size: 100px">
Harry
</b>
</li>
<li><i>Ron</i></li>
<li><strong>Hermione</strong></li>
</ul>
<input autofocus />
</div>
</template>
Use none, one or many attributes is the technique used to create dynamic components.
Consider how adding this type
attribute & value changes the behavior of the input
tag for example.
<template>
<div>
<h1 :style="{'color': 'red'}">Wizards</h1>
<ul>
<li>
<b style="font-size: 100px">
Harry
</b>
</li>
<li><i>Ron</i></li>
<li><strong>Hermione</strong></li>
</ul>
<input autofocus type="number" /> !!!!!
</div>
</template>
Separate HTML from CSS when possible.
Use a style
tag to extract CSS.
Placing CSS there helps when components grow & makes template easier to reason about.
<template>
<div>
<h1>Wizards</h1>
<ul>
<li>Harry</li>
<li>Ron</li>
<li>Hermione</li>
</ul>
<input autofocus />
</div>
</template>
<style>
h1 {
color: red;
}
b {
font-size: 30px;
}
</style>
The script
tag is where we write JS
code.
We can define variables there for example, in this case wizard
.
<script setup>
const wizard = 'Programmer'
</script>
<template>
<!-- html... -->
</template>
<style>
/* css... */
</style>
JS defined in the script tag can be used in the template using handlebars/mustaches.
<script setup>
const wizard = 'Programmer'
</script>
<template>
<div>
<h1>Wizards</h1>
<ul>
<li>Harry</li>
<li>Ron</li>
<li>Hermione</li>
<li>{{ wizard }}</li>
</ul>
<input autofocus />
</div>
</template>
<style>
h1 {
color: red;
}
b {
font-size: 30px;
}
</style>
JS is also used to programmatically render HTML with the help of directives.
A simple directive is v-if
.
It evaluates a JS statement and renders the HTML if the JS expression returns true
.
<script setup>
const expression = 1 === 1
</script>
<template>
<div v-if="expression">
True
</div>
</template>
Like all if
conditionals else
statements can be added.
It's v-else
in Vue.
<script setup>
const expression = 1 === 'one'
</script>
<template>
<div v-if="expression">
False
</div>
<div v-else>
True
</div>
</template>
And with the v-else-if
an infinite number of possibilities results are possible.
<script setup>
const value = 1
</script>
<template>
<div v-if="value === '1'">
False
</div>
<div v-else-if="value === 'One'">
False
</div>
<div v-else-if="value === 'Uno'">
False
</div>
<div v-else-if="value === 'Un'">
False
</div>
<!-- how many more ways might we be wrong...? 🤔 -->
</template>
There are many directives.
They all start with ' v-
'.
v-if
v-else-if
v-else
v-for
v-html
v-text
- etc...
Separate JS from HTML when possible.
Doing so makes your code easier to understand.
To do so define a script
which will hold your JS.
<script setup>
</script>
<template>
<div>
</div>
</template>
Now define the variables you want to use in your templates.
<script setup>
const pronoun = 'Harry'
</script>
<template>
</template>
Now you can interpolate the vars into the template with mustache/handlebar syntax.
<script setup>
const pronoun = 'Harry'
</script>
<template>
<div>
{{ pronoun }}
</div>
</template>
You can create something magical by using multiples vars.
<script setup>
const pronoun = 'Harry'
const verb = ' is '
const adjective = 'magical'
</script>
<template>
<div>
{{ pronoun }} {{ verb }} {{ adjective }}
</div>
</template>
Combing lists & loops in a v-for
creates enchanting possibilities.
<script setup>
const pronoun = 'Harry'
const verb = ' is '
const adjective = 'magical'
const spells = ['HTML', 'JS', 'Vue']
</script>
<template>
<div>
{{ pronoun }} {{ verb }} {{ adjective }}
</div>
<ul>
<li
v-for="spell of spells"
>
<div>{{ spell }}</div>
</li>
</ul>
</template>
Especially if you combine new skills with previous abilities like the v-if
directive.
<script setup>
const pronoun = 'Harry'
const verb = ' is '
const adjective = 'magical'
const spells = ['HTML', 'JS', 'Vue']
</script>
<template>
<div>
{{ pronoun }} {{ verb }} {{ adjective }}
</div>
<ul>
<li
v-for="spell of spells"
>
<div v-if="spell === 'Vue'" style="color: green">{{ spell }}</div>
<div v-else>{{ spell }}</div>
</li>
</ul>
</template>
Add a ternary
operator to simplify the your code a bit.
<script setup>
const pronoun = 'Harry'
const verb = ' is '
const adjective = 'magical'
const spells = ['HTML', 'JS', 'Vue']
</script>
<template>
<div>
{{ pronoun }} {{ verb }} {{ adjective }}
</div>
<ul>
<li
v-for="spell of spells"
>
<div :style="{color: spell === 'Vue' ? 'green' : 'black'}">{{ spell }}</div>
</li>
</ul>
</template>
We could have concatenated our vars earlier as well.
<script setup>
const pronoun = 'Harry'
const verb = ' is '
const adjective = 'magical'
const spells = ['HTML', 'JS', 'Vue']
</script>
<template>
<div>
{{ pronoun + verb + adjective }}
</div>
<ul>
<li
v-for="spell of spells"
>
<div :style="{color: spell === 'Vue' ? 'green' : 'black'}">{{ spell }}</div>
</li>
</ul>
</template>
And lastly, we can define functions in scripts and invoke them in the templates.
<script setup>
const pronoun = 'Harry'
const verb = ' is '
const adjective = 'magical'
const spells = ['HTML', 'JS', 'Vue']
function hotSpell(spell) {
return spell === 'Vue' ? 'green' : 'black'
}
</script>
<template>
<div>
{{ pronoun + verb + adjective }}
</div>
<ul>
<li
v-for="spell of spells"
>
<div :style="{color: hotSpell(spell)}">{{ spell }}</div>
</li>
</ul>
</template>
Combining HTML, JS, directives, handlebars & loops allows
As data changes UI should as well.
But to make the UI reflect changes to data in real time we have to use binding.
For example, here we define num
and increment it every second with an an interval.
We expect the update to num
every second to update the UI.
<script>
let num = 0
setInterval(() => {
num += 1
}, 1000)
</script>
<template>
<div
:class="{
'text-green-400': (num % 2) == 0,
}"
>
Color {{ num }}
</div>
</template>
Yet the UI doesn't update every second like you'd think.
However if you log num
in the interval and inspecting it in the dev tools you'll see it updates.
The issue can be solved by binding num
with ref()
provided by Vue.
First you have to update the way you define num
.
When defining num
invoke ref()
with it's initial value, 0
, and set that to num
<script>
let num = 0 // [!code --]
let num = ref(0) // [!code ++]
setInterval(() => {
num += 1
}, 1000)
</script>
<template>
<div
:class="{
'text-green-400': (num % 2) == 0,
}"
>
Color {{ num }}
</div>
</template>
Then change the update syntax by adding a .value
<script>
let num = ref(0)
setInterval(() => {
num += 1 // [!code --]
num.value += 1 // [!code ++]
}, 1000)
</script>
<template>
<div
:class="{
'text-green-400': (num % 2) == 0,
}"
>
Color {{ num }}
</div>
</template>
Now you'll see that the UI updates in real time as you expect.
- Initialize vars with
ref()
.
- Reference and update the
.value
property of vars you've bound.
- In templates vars do not require
.value
.
Color {{ num }}
'text-green-400': (num % 2) == 0,
Learn how to organize components in your projects next.