First of all, are you aware the Vue version names are mostly derived from manga / anime titles? Well I just found this out, interesting.
By now most of you have probably heard of VueJs 3 which was released in 2020. With the release of VueJs 3, the Composition API was introduced as a new way to write components. The Composition API is an alternative to the Options API, which is the "traditional" way of writing Vue.js components.
In this article, we will dive into the VueJs Composition API and explore its basic concepts, how to create and use composition functions, how to compose multiple functions, advanced techniques, and compare it to the Options API. By the end of this article, you'll have a good understanding of the Composition API and be able to write more modular and scalable Vue.js components.
Introduction to the Composition API
Prior to the introduction of the Composition API, Vue developers used the options API to build Vue components. This API defines a set of properties and methods that determine how a component behaves. It is relatively easy to use but can become difficult to manage and maintain as the application grows more complex.
The Composition API provides a new way to build Vue components that is more flexible, scalable, and organized. Instead of defining a component's properties and methods in a single options object, the Composition API allows you to split a component's logic into reusable and composable functions, called "composables". These composables can be used across multiple components, making code sharing and maintenance much easier.
To illustrate the differences between the options API and the Composition API, let's take a look at some code samples. First, we'll examine a sample Vue component written using the options API, and then we'll see how the same component can be written using the Composition API.
Options API
<script>
export default{
data(){
return{
greeting: "Hello reader"
}
},
methods: {
greet(){
this.greeting = "Hello, welcome to the Vue Options API"
}
},
mounted(){
console.log(this.greeting)
}
}
</script>
<template>
<button @click="greet">{{greeting}}</button>
</template>
Composition API
<script setup>
import { ref, onMounted } from 'vue'
// your reactive state
const greeting = ref("Hello reader")
//modifies greeting state and updates
function greet() {
greeting.value = "Hello, welcome to the Vue Composition API"
}
// lifecycle hook
onMounted(() => console.log(greeting.value)
</script>
<template>
<button @click="greet">{{ greeting }}</button>
</template>
Can you spot the differences 🤔. There are some interesting keywords in the composition API such as ref
, value
onMounted
and setup
. We are going to look at what these mean in a moment but before that let's look at some of the advantages the composition API brings to the table in terms of code reusability and organization.
Code Organisation and Reusability
Imagine having a large codebase with complex components, managing component logic can be challenging. The Options API often separates data and functionality into different options, with the data in the data
object, functions in the methods
object and lifecycle hooks grouped together. As the codebase grows, this separation can lead to difficulty navigating and organizing the code.
The Composition API provides a fix for this issue, allowing developers to group feature logic in a single setup
function. With this approach, related feature code can be easily grouped and managed, greatly improving the readability and maintainability of the codebase. By using the Composition API, developers can more easily manage complex components and ensure that their codebase remains organized as it grows. The Vue3 documentation does a good job of explaining this with a good image with color bands that show related logic and how they are grouped.
What of code reusability, I remember the go-to feature for that in Vue2 was mixins
, Vue3 introduced Composable functions that allow you to extract logic and inject it into another setup function in another component. Sounds awesome right 😁, and sounds similar to hooks in React. Moving on let's look at some other concepts.
As a side note you can use both the options API and Composition API in one project
Reactivity
What is reactivity? and how is this handled in the composition API. It's quite simple, a change is made to a component state, and the view gets updated. That is simple enough but it is important to understand how to implement reactivity with the composition API. The composition API allows you to make your components reactive by using the ref
and reactive
properties in the <script setup>
.
Variables in the <script setup>
by default are not reactive, take a look at the code sample below:
<script setup>
...
let name = "Kofi"
function changeName(){
name = "Kwame"
}
...
</script>
<template>
<h1> My name is {{name}} </h1>
<button @click="changeName">Click</button>
</template>
When the button is clicked the name in the view will still be Kofi
and there would be no update, the reason once again is the name
variable is not reactive by default. How then do we make the name
variable reactive 💭🤔? We can use ref
to make the name
variable reactive by wrapping the name
variable with ref
and grab the value
on the name
ref
like so:
<script setup>
import {ref} from 'vue'
...
let name = ref("Kofi")
function changeName(){
name.value = "Kwame"
}
...
</script>
<template>
<h1> My name is {{name}} </h1>
<button @click="changeName">Click</button>
</template>
Now the variable name
is reactive and a click of the button would change the value of name
from Kofi
to Kwame.
Using ref
instructed vue to watch the name
variable and update the view when it is mutated, cool right 😎!
There is a second way of making variables reactive. This is by using reactive
like so:
<script setup>
import {reactive} from 'vue'
...
let person = reactive({name: "Kofi", height: 59})
function changeName(){
person.name = "Kwame"
}
...
</script>
<template>
<h1> My name is {{person.name}} </h1>
<button @click="changeName">Click</button>
</template>
You can use it the same way as refs but there are some differences. In the example above, in the methodchangeName,
I went ahead and updated the name by writing person.name
, using a ref
would have been person.value.name
. You do not have to use the .value
notation to access the value when using reactive. That is the first difference. You are probably wondering why I used a reference type in this example rather than a primitive type. Well, the reason is reactive
does not work on primitive types so doing something like this would not work!! :
<script setup>
import {reactive} from 'vue'
...
//this wont work!!!!!!
let name = reactive("Kofi")
function changeName(){
name = "Kwame"
}
...
</script>
<template>
<h1> My name is {{name}} </h1>
<button @click="changeName">Click</button>
</template>
So remember, primitive types do not work with reactive
. I would advise you just stick with refs
, they just work.
Lifecycle Hooks
Vue.js 3 provides a lifecycle system for components, which allows developers to manage the component's behavior and state during different stages of its lifecycle. Each component in Vue.js 3 goes through a series of phases, starting from initialization, mounting, updating, and finally unmounting. The names of the lifecycle hooks in the Vue3 composition API have slightly different names, here are a couple of them:
onBeforeMount
: This hook is called before the component is mounted to the DOM.onMounted
: This hook is called after the component has been mounted to the DOM.onBeforeUpdate
: This hook is called before the component re-renders.onUpdated
: This hook is called after the component has been re-rendered.
Computed Properties, Watch and WatchEffect
Computed
Some properties require some calculations to be applied to them before they get displayed in the view, doing these calculations in the view/template makes your code clumsy and sometimes difficult to read. The right way to run some logic on a property before displaying it is to use computed
.
In the Composition API, a computed property is created by calling the computed
function with a getter function as its argument. The getter function can then access reactive data properties and use them to calculate and return a new value. The computed
function returns a Ref
object that stores the computed value and automatically updates it when any of the reactive dependencies change.
Here is an example of using computed with the composition API:
<script setup>
import { ref, computed } from "vue";
const count = ref(5);
const doubledCount = computed(() => count.value * 2);
</script>
<template>
<div>
<p>The count is: {{ count }}</p>
<p>The doubled count is: {{ doubledCount }}</p>
</div>
</template>
In the above example, we have a component that defines a reactive data property count
and a computed property doubledCount
. The count
property is initialized with a value of 5
using the ref
function, while the doubledCount
property is defined using the computed
function and calculates the value of count
multiplied by 2.
In the template, we use interpolation to display the values of count
and doubledCount
. The count
property is displayed using {{ count }}
, while the doubledCount
property is displayed using {{ doubledCount }}
.
Whenever the count
property is updated, the doubledCount
property will automatically recalculate its value based on the new value of count
. This reactive behavior is provided by the Composition API's computed
function, which automatically tracks the dependencies of the computed property and updates its value whenever any of them change.
Watch and WatchEffect
There are situations where you want to observe a property for changes and invoke a function whenever those changes occur. Well, VueJs has you covered🤩. Let us look at how to achieve that.
watch
and watchEffect
are two functions that allow you to observe changes to a property or expression in a reactive way. When the observed property or expression changes, the specified function is executed automatically. watch
takes two arguments: the property or expression to watch and a callback function to run when it changes. watchEffect
takes a single argument: a callback function containing the property or expression to watch. The main difference between the two is that watch
provides access to both the old and new values of the property or expression, while watchEffect
only provides access to the current value. Both functions are useful for creating more advanced reactive behavior and responding to changes in your component's data and state.
Here is an example:
<script setup>
import { ref, watch, watchEffect } from 'vue';
const message = ref("");
// using watch to track changes in the message value
watch(message, (newVal, oldVal) => {
console.log(`Message changed from "${oldVal}" to "${newVal}"`);
});
// using watchEffect to log the initial value of message
watchEffect(() => {
console.log(`Initial message value: "${message.value}"`);
});
</script>
<template>
<div>
<input v-model="message" placeholder="Type a message">
<p>Message: {{ message }}</p>
</div>
</template>
In this example, we use both watch
and watchEffect
to track changes in the message
value. The watch
function is used to log a message to the console whenever the message
value changes, while watchEffect
is used to log the initial value of message
when the component is mounted. This approach allows us to track changes to the message
value and perform any necessary actions in response, as well as log important information about the state of the component.
Conclusion
In conclusion, the Composition API is a powerful addition to Vue.js that allows for more flexibility and organization in your code. It enables developers to write more modular, reusable code that is easier to understand and maintain. The Composition API introduces new features such as ref
, reactive
, computed
, watch
, and more, which allow for reactive programming and advanced data manipulation. In Part II of this series, we will explore Composables in the Composition API, which will take our understanding of the Composition API to the next level. Stay tuned!