Complete Guide Vue 3 — Composition API

Part 0 — Introduction Composition API

at DANA we are always improving our code quality & app performance. This is important for us to do. And the good news is that in Vue 3 there is a Composition API feature that aims to overcome the limitations and weaknesses of the Options API. We will learn more about Vue 3 Composition API, to make it easier for us to understand the Composition API, I have created a Todo App project and you can directly see my code on GitHub in this repo. You can also access the Todo app demo that has been created at this link. In Composition API we will learn Ref, Reactive, toRefs, Methods, Computed Getter & Setter, WatchEffect, Watch, Lifecycle, Component (Props & Emit).

Composition API in Vue 3 is optional, at the time this article was written programmers could still use the Options API in Vue 3 for Web App Development. The Composition API was created to overcome the limitations of Option API. Composition API feels useful in large and complex applications. This is because the concept of Composition API is to separate several logical concerns and even make the logic reusable. An example is :

setup () {
const { todos } = useListTodo()
const { addTodo} = useCrudTodo()
return {
todos,
addTodo
}

function useListTodo () {
const todos = ref('')
return {
todos
}
}
function useCrudTodo () {
const addTodo = (e) => {
console.log(e)
}

return {
addTodo
}
}
}

The code above is an example for separating logical concerns, while for how to make a function reusable, suppose we want to create a function to store data into LocalStorage.

export default function saveDataToLocalStorage(listTodo) {
localStorage.setItem('todos', JSON.stringify(listTodo))
}

we save the code in the filesave-local-storage.js , then we can use that function on all components, an example is as follows:

//import file save-local-storage.js
import saveToLocalStorage from '../components/save-local-storage.js'
const addTodo = () => {
if(!todo.value) return
todos.list.unshift({
activity: todo.value,
isDone: false
})
todo.value = ''
saveToLocalStorage(todos.list)
}

Install Vite

Vite was created directly by the creator of Vue.js namely Evan You, Vite mission is to speed up the development process. If we use Vue CLI, the problem is we have to wait for all Applications in Vue to finish the bundle, so it will be quite a waste of development time, especially if the Application is quite complex and large. Unlike Vue CLI, Vite will only compile the files that are needed, namely the files that have changed. This will speed up the development process.

At the time this article was written, we had to install node in local with version >=12.0.0. And then we can install Vite with npm or yarn.

# npm 6.x
npm init vite@latest my-vue-app --template vue
# npm 7+, extra double-dash is needed:
npm init vite@latest my-vue-app -- --template vue
# yarn
yarn create vite my-vue-app --template vue
# pnpm
pnpm create vite my-vue-app -- --template vue

Install & Config Tailwind CSS on Vite

If we use nom to install Tailwind CSS, then go to the project directory and run the following command in terminal

npm install -D tailwindcss postcss autoprefixernpx tailwindcss init -p

the above command will generate a filetailwind.config.js and postcss.config.js. then on filetailwind.config.js edit and settings as follows:

tailwind.config.js

create fileindex.css in directorysrc and add directive@tailwind for each tailwind layer.

@tailwind base; 
@tailwind components;
@tailwind utilities;

Last import file ./src/index.css in file./src/main.js

main.js

we can run Vite for development and use Tailwind CSS with the commandnpm run dev. As for production, we use the commandnpm run build , The command will generate all the assets needed and store them indist directory.

Part 1 — Ref, Reactive & toRefs

Ref, Reactive & toRefs is an important parts that we should know in Composition API. We will learn more about functions and examples of their use.

Setup

The Composition API uses the main function namedsetup , function setup contains all our logic.

export default {
setup(props, context) {
// Attributes (Non-reactive object, equivalent to $attrs)
console.log(context.attrs)
// Slots (Non-reactive object, equivalent to $slots)
console.log(context.slots)
// Emit events (Function, equivalent to $emit)
console.log(context.emit)
// Expose public properties (Function)
console.log(context.expose)
}
}

function setup has two arguments namelyprops dan context . props inside of a setup function are reactive and will be updated when new props are passed in. While context the argument is a normal object and not reactive, By using ES6 we can destructuring context as follows:

export default {
setup(props, { attrs, slots, emit, expose }) {
...
}
}

Keep in mind in Composition API we don't definedata, methods and computed all will be replaced with concept function.

Ref

Ref is used to creating reactive and mutable variables. The ref can only be used for primitive data such as boolean, string and integer. Object Ref has one property .value to set and get object Ref.

import { ref } from 'vue'export default {
setup(){
const todo = ref('todo is empty')
console.log(todo.value) // todo is empty

todo.value = 'I want to finish Vue course'
console.log(todo.value)//I want to finish Vue course

return {
todo
}
}
}

To access Ref in HTML template is:

<input v-model="todo" placeholder="Add anything..." type="text" name="todo"/>{{ todo }}

Reactive

Unlike Ref which can only be used for primitive data, Reactive can be used for objects. as an example:

import { reactive } from 'vue'export default {
setup(){
const todos = reactive({
activity: "I want to finish Vue Course",
isDone: false
})
setTimeout( () => {
todos.activity = "I want to finish Reactjs Course",
todos.isDone = false
}, 2000)
return {
todos
}
}
}

if we use Ref, the data in the code above will not be reactive. Therefore we can use Reactive. Meanwhile to access Reactive in HTML template are:

{{ todos.activity }} - {{ todos.isDone }}

toRefs

toRefs is used to convert a Reactive object into a plain object. To better understand toRefs we give an example case study. Let’s say we want to access Reactive on an HTML template like this:

{{ activity }} - {{ isDone }}

Whereas in Composition API the code is like this:

import { reactive } from 'vue'export default {
setup(){
const todos = reactive({
activity: "I want to finish Vue Course",
isDone: false
})
setTimeout( () => {
todos.activity = "I want to finish Reactjs Course",
todos.isDone = false
}, 2000)
return {
...todos
}

}
}

By only using javascript spread syntax without using toRefs, then we will lose its reactivity. So the data will not change, even though we will change the data after 2 seconds. To solve this case we use toRefs.

import { reactive, toRefs } from 'vue'export default {
setup(){
const todos = reactive({
activity: "I want to finish Vue Course",
isDone: false
})
setTimeout( () => {
todos.activity = "I want to finish Reactjs Course",
todos.isDone = false
}, 2000)
return {
...toRefs(todos)
}

}
}

toRef

Unlike toRefs which is used for Reactive, toRef is used to convert a single reactive object property to Ref. Here’s the difference

toRef

const state = reactive({
foo: 1,
bar: 2
})
const fooRef = toRef(state, 'foo')
/*
fooRef: Ref<number>,
*/

toRefs

const state = reactive({
foo: 1,
bar: 2
})
const stateAsRefs = toRefs(state)
/*
{
foo: Ref<number>,
bar: Ref<number>
}
*/

Part 2 — Methods, Computed Getter & Setter

Methods

Unlike Option API, we have to declare function inside methods, in Composition API we don’t need to, see the difference.

data() {
return {
count: 4
}
},
methods: {
increment() {
// `this` will refer to the component instance
this.count++
}
}

By using Composition API will be shorter.

setup(){
const count = ref(0)
const increment = () => {
count.value++
}
return {
count,
increment
}
}

Computed Getter & Setter

To use Computed we have to call computed in Vue. For exampleimport { computed } from ‘vue’ For an example of using a Computed getter & setter, see the code below.

import { ref, computed } from 'vue'export default {
setup(){
const count = ref(1)
const result = computed({
get: () => count.value + 10,
set: val => {
console.log('val',val) //0
count.value = val - 5
}
})

result.value = 0
console.log("count", count.value) //-5
console.log("result", result.value) //5
return {
count,
result
}
}
}

To call Computed in HTML template, it’s easy:

<template>
{{ count }} = {{ result }}
</template>

But if you don’t want to use computed getter & setter, here’s an example:

import { ref, computed } from 'vue'export default {
setup(){
const count = ref(1)
const result = computed(() => {
return count.value+10
})

result.value = 0
console.log("count", count.value) // 1
console.log("result", result.value) // 11

return {
count,
result
}
}
}

Part 3 — Watch & WatchEffect

watch

watch is useful for monitoring changes in state, we can do something to the state if there is a change. inwatch we can also get old value or previous value.

To use watch & watchEffect we import first:

import { watchEffect, watch } from 'vue'

example of using watch

watch(todo, (newValue, prevValue) => {
console.log("todo", todo.value)
console.log("prev", prevValue)
})

To monitor state changes in more than one, the example is:

watch([todo, todos], (newValue, prevValue) => {
console.log("todo", todo.value)
console.log("todos", todos)
console.log("new value", newValue)
console.log("prev value", prevValue)
})

watchEffect

watchEffect has the same function namely monitoring state changes. The difference is watchEffectis suitable for monitoring more than one state. which should be noted watchEffect can not access old values or previous values in the state.

watchEffect( () => {
console.log("todo", todo.value)
console.log("todo list", todos.list)
})

especially for debugging, we can use optionsonTriggerand onTrackbut keep in mind this only applies to development, if in production onTrigger and onTrack will not run.

watchEffect(
() => {
console.log("todo", todo.value)
},
{
onTrigger(e) {
console.log("onTrigger", e)
},
onTrack(e) {
console.log("onTrack", e)
}
}
)

Besides that, there are also optionsflush , optionsflush the default value ispre which means it will be executed before the component is rendered we can change the value to post which means it will be executed after the component is rendered.

watchEffect(
() => {
console.log("todo", todo.value)
},
{
flush: 'post'
}
)

Part 4 — Lifecycle Hooks

Lifecycle in Composition API is not too different from Options API, there are several lifecycles that we need to understand:

  • onBeforeMount - called before mounting begins
  • onMounted - called when the component is mounted
  • onBeforeUpdate - called when reactive data changes and before re-render
  • onUpdated - called when reactive data changes and after re-render
  • onBeforeUnmount - called before the Vue instance is destroyed
  • onUnmounted - called after the instance is destroyed
  • onActivated - called when a keep-alive component is activated
  • onDeactivated - called when a keep-alive component is deactivated
  • onErrorCaptured - called when an error is captured from a child component

maybe some of us will ask, in Options API there are created and beforeCreated lifecycle is Composition API removed created and beforeCreated lifecycle? The answer is that created and beforeCreated lifecycles are replaced with setup() here’s an example of how to use them.

//Options API
export default {
data() {
return {
val: 'hello world'
}
},
created() {
console.log('Value of val is: ' + this.val)
}
}

if you use Composition API it will look like this:

import { ref } from 'vue'

export default {
setup() {
const val = ref('hello world')
console.log('Value of val is: ' + val.value)
return {
val
}
}
}

Keep in mind that if you want to use onMounted onUpdated other hooks, don’t forget to import them first.

import { onMounted, onUpdated } from 'vue';

In addition, for debugging we can use onRenderTriggered and onRenderTriggered hooks, for more details we can read the documentation here.

Part 5 — Component (Props & Emit)

props and emit are used for passing data between components. props are used to send data from the parent component to the child component. Meanwhile emit can be used as a trigger event for passing data from the child component to the parent component.

//called from parent component
<list-todo
:todos="todos.list"
@delete-todo="deleteTodo"
/>

we can use access props and use emit in child component as follows:

import { onMounted } from "vue"
export default {
props: {
todos: {
type: Array,
default: [],
}
},
setup(props, { attrs, slots, emit }) {
const handleDeleteTodo = (index) => {
emit('delete-todo', index)
}

onMounted(() => {
console.log("data from parent : ", props.todos)
})

return {
handleDeleteTodo,
}
}
}

need to remember that setup() has two arguments, namely props and context . context can be destructured to { attrs, slots, emit, expose } we can emit using context

emit('eventName', [argumen])

maybe that’s what I can write, you can read the full code on my GitHub at this link. Hope it is useful. If you have any questions or suggestions, please contact me at https://dikiharifwibowo.github.io/.

--

--

--

A writing media straight from our kitchen. Discover insights on digital product development, user experience design, and tech engineering.

Recommended from Medium

Bring RethinkDB’s realtime magic to the frontend with GraphQL

Proud of you Follow Follow Follow

JavaScript Code Styling Best Practices — If’s and Mixing Characters

Composing React Components

Abstract Tree

Coding Challenge: Swapping Nodes in a Linked List and Swap Nodes in Pairs

Making JavaScript objects immutable

Create a Typing Test with React and JavaScript

Gatsby.js — Scroll Position, Dynamic Navigation, and Link States

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Dikih Arif Wibowo

Dikih Arif Wibowo

JS Enthusiast | dikiharifwibowo.github.io

More from Medium

How to create and publish VueJS component library

How to Easily Create Beautiful App Bars with Vuetify

Top 10 Vuejs Technology Benefits You Must Know

Vue, Vuetify.js & dark mode