A look at the VueJs 3 Composition API - Part I

Nunya KlahNunya Klah
9 min read

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:

  1. onBeforeMount: This hook is called before the component is mounted to the DOM.

  2. onMounted: This hook is called after the component has been mounted to the DOM.

  3. onBeforeUpdate: This hook is called before the component re-renders.

  4. 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!

1
Subscribe to my newsletter

Read articles from Nunya Klah directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Nunya Klah
Nunya Klah

I am a software developer, specializing in frontend development using VueJs. I have interests in Go and Python and I have experience working with Scala and Elm.