Vue 3 composition API guide tutorial with snippet examples
Table of contents
- What is the Vue 3 composition API?
- What is
ref()
in Vue 3? - How to update
ref()
values - Vue 3's
computed()
function explanation - How to use Vue 3's
watch()
andwatchEffect()
functions - I'll start with
watchEffect()
: - And how to use
watch()
in Vue 3: - When to use
watch()
instead ofwatchEffect()
: - How to use template refs in Vue 3
- How to use
<script setup>
in Vue 3 - What is so special about
setup()
? - What lifecycle hooks are available in Vue 3's
setup()
function? - Making reactive variables in Vue 3 with composition API
- Using
reactive()
with objects in Vue 3 composition API - Read more about Vue 3
What is the Vue 3 composition API?
Vue 1 and 2 both used the options API. If you have read any Vue tutorial in the last few years, you will be familiar with it (when you had the data()
function, computed
object with computed property functions, functions such as mounted()
, unmounted()
etc).
Vue 3 still fully supports the options API (and it is recommended to use for simple projects). But it has introduced a new way of doing things called the composition API.
(The new Vue composition API is quite similar to React hooks.)
A simple example of using the Vue composition API looks like this:
<template>
<p>Hello {{ name }}</p>
<button @click="changeName">change name</button>
</template>
<script>
import { ref, computed } from 'vue';
export default {
setup() {
const name = ref('Your Name');
// computed property, calculating the number of characters
// on load it will be 9 ('Your name'). Once the button is
// pressed it will be 7 ('My name'):
const nameLength = computed(() => name.value.length);
function changeName() {
name.value = 'My name';
}
return { name, changeName, nameLength };
}
};
</script>
Explanation of the composition API: The setup()
function sets up a couple of variables (name
and the changeName
function). These are then returned near the end (return { name, changeName }
).
The <template>...</template>
section will be used during render - and the crucial part is that it has access to name
, changeName
, and nameLength
(the variables that were returned in setup()
).
ref()
in Vue 3?
What is The ref()
function (imported at the top of the script section: import { ref } from 'vue'
) is a function that will make a variable reactive.
(ref()
should be used with primitives, such as numbers/strings. Read on to also find out about reactive()
)
You can imagine that name
is structured like this:
{
value: 'Your name'
}
This is why when the changeName()
function wants to update the name, it must set `name.value = 'My name'.
(Updating just name = 'A new name'
would not work.)
One of the magic things that Vue 3 does is it lets you access name
directly in the <template>
section.
(Notice that you don't need to do Hello {{ name.value }}
- instead you can write Hello {{ name }}
. This is handled automatically by Vue's templating system.
ref()
values
How to update Always remember to set the .value
attribute, or it will not work.
let yourCounter = ref(1234); // or const!
// correct ways to update:
yourCounter.value++;
yourCounter.value = 456;
// incorrect ways:
yourCounter++;
yourCounter = 789;
computed()
function explanation
Vue 3's A line from the example above that may look familiar to Vue 2 developers is the computed()
function.
This is very similar to the computed
object in the options API. This is how the computed property would look in the options API in Vue 2:
{
// ...
computed: {
nameLength() {
return this.name.length
},
// ...
}
}
But in Vue 3's composition API lets you set up your computed properties in the setup()
function.
Here is the computed code again, written in a slightly different format:
const name = ref('Something');
function calculateNameLength() {
return name.value.length;
}
const nameLength = computed(calculateNameLength);
// Note: like ref(), you must use .value with computed
// values within setup()
const isNameLengthLong = computed(() => nameLength.value.length > 5)
The calculateNameLength
function will be run:
- initially run straight away, and it will return the length of
'Something'
(which is9
). - And then again in the future any time that the `name` value is updated.
Note: if you need to access a computed property value in setup()
, then you must use .value
.
If you need to access a computed property within <template>
, you do not need to add .value
as it is automatically unwrapped by Vue 3.
watch()
and watchEffect()
functions
How to use Vue 3's In Vue 2 (or Vue 3 using the options API) you have probably used the watch
feature.
It looked something like this
{ // ...
data() {
return { yourCounter: 0, anotherCounter: 100 }
},
methods: {
incrementCount() {
this.yourCounter++;
}
},
watch: {
yourCounter(newValue, prevValue) {
console.log("this.yourCounter was just updated!");
console.log(newValue)
console.log(prevValue)
this.anotherCounter--;
}
},
// ... }
Every time this.yourCounter
is mutated, it will run the function yourCounter()
which will console.log()
some output and decrement this.anotherCounter
.
In Vue 3 there are a couple of ways to do the same thing. The easiest is with watchEffect()
but you can also use watch()
.
watchEffect()
:
I'll start with import { watchEffect } from 'vue'
const count = ref(0)
watchEffect(() => console.log(count.value))
// initially logs 0
setTimeout(() => {
count.value++
// logs logs 1
}, 100)
watchEffect()
will initially run the function you pass to it (() => console.log(count.value)
). It will then track all dependencies (in this case count
was accessed, so that is a dependency).
After initially running, whenever count
is updated it will rerun the function again.
watch()
in Vue 3:
And how to use const count = ref(0)
watch(count, (value, prevValue) => {
console.log('Count was updated');
console.log({value, prevValue});
})
watch()
is similar to watchEffect()
, but has a bit more code to set it up. It will also only watch the value variable passed in the first argument.
watch()
instead of watchEffect()
:
When to use - Watch it lazily (do not immediately run the function)
- Only re-run the function when a specific variable was updated
- Have access to the current (new) value and the previous value
For most typical uses you can normally get away with using watchEffect
.
How to use template refs in Vue 3
In Vue 2 (or Vue 3 with the options API) you could use this.$refs.something
to access a template ref (such as <button ref="something">...</button>
.
In Vue 3's composition API you cannot use this
in setup()
.
This is how you use refs in Vue 3 with setup()
:
<template>
<button ref="btnRef">Click me</button>
</template>
<script>
import { ref, onMounted } from 'vue';
export default {
setup() {
const btnRef = ref(null);
onMounted(() => {
console.log(btnRef.innerHTML);
// logs the HTML of the <button>
});
return { btnRef }
}
}
</script>
To use refs
in Vue 3 you must import ref
, then create a variable from ref(null)
. Pass that to the object that setup()
references, and use the same variable name as the ref in the HTML.
<script setup>
in Vue 3
How to use The Vue 3 script setup syntax is syntactical sugar that will make writing Vue 3 code a bit easier, and it ultimately compiles to standard Vue 3 code.
As well as export default { ... }
(like most examples of Vue 3 on this page), you can also use the Vue 3 script setup way of doing it:
<template>
<h1>{{ person.name }}</h1>
<h2>{{ person.age }}</h2>
<button @click="incrementAge">+1</button>
</template>
<script setup>
import { reactive } from 'vue';
export const person = reactive({
name: "webdevetc",
age: 99
})
export const incrementAge = () => person.age++
</script>
Using the <script setup>
syntax you can just export all of your variables, without having to export an object.
For more advanced use of <script setup>
see here.
<script setup="props">
import { watchEffect } from 'vue'
watchEffect(() => console.log(props.yourProp))
</script>
setup()
?
What is so special about The setup()
function is new to Vue 3. This is where you can combine all of your data properties, computed properties and much more.
There are a few important things to know about setup()
:
- You can use imported variables and functions (I will show some examples later) - this means you can easily share code between components. Before Vue 3 it was messy to do this (e.g. by mixins)
setup()
is run before any of the normal lifecycle hooks.
setup()
function?
What lifecycle hooks are available in Vue 3's You have access to most of Vue 3's life cycle hooks in setup()
. Here is an example:
import {
onActivated, onBeforeMount, onBeforeUnmount, onBeforeUpdate,
onDeactivated, onErrorCaptured, onMounted, onUnmounted, onUpdated,
} from 'vue';
export default {
setup() {
onBeforeMount(() => console.log('before mount'));
onMounted(() => console.log('mounted'));
onBeforeUpdate(() => console.log('before update'));
onUpdated(() => console.log('updated'));
onBeforeUnmount(() => console.log('before unmount'));
onUnmounted(() => console.log('unmounted'));
onActivated(() => console.log('activated'));
onDeactivated(() => console.log('deactivated'));
onErrorCaptured(() => console.error('error captured'));
onRenderTracked(() => console.log('render tracked'));
onRenderTriggered(() => console.log('render triggered'));
}
};
Note: setup()
is called between beforeCreate()
and created()
, so there is no need to use them. Just put whatever code you would need in those functions in setup()
itself.
Making reactive variables in Vue 3 with composition API
If you have been following along, you have already seen ref()
.
With ref()
any changes to it within setup()
must use the .value
syntax:
{
setup() {
const yourAge = ref(30);
// notice to increment it you must
// update `yourAge.value`:
const incrementAge = () => yourAge.value++
return { yourAge, incrementAge }
}
}
(Within your <template>
block you can use it without .value
as Vue will automatically unwrap it. )
ref()
should be used with primitive data types (undefined
, Boolean
, Number
, String
, BigInt
, Symbol
).
But for non primitive data types (which in 99% of the time will be Object
) you should use reactive()
.
TODO - explain the data structure of ref()
reactive()
with objects in Vue 3 composition API
Using If you want to work with some reactive data is not a primative (such as objects) then you should use reactive()
.
(reactive()
is similar to Vue 2's Vue.observe()
)
You cannot use reactive()
with primitives, it won't work
Unlike ref()
which makes you access .value
within setup, you can access it as if it was a regular object:
import { reactive } from 'vue';
{
setup(){
const person = reactive({
age: 30,
nationality: 'British',
});
const personIsAdult = computed(() => person.age >= 18)
return { person, personIsAdult }
}
}
Read more about Vue 3
Read more about Vue 3 and how to use Vue 3 here.
TODO - more coming soon