(Partially) Different Way to Build Component using Vue Composition API
In September 2020, Vue.js released their latest version, Vue 3, which gave us quite a lot of changes. Yet in this article, I will focus on one of the significant changes, namely the Vue Composition API. But before that, here’s a summary of some of the updates to Vue 3 and why we should give it a try.
Why Vue 3?
- Smaller + faster
- Improved Typescript support
- Composition API
- Multiple root nodes on template (we can only have one root on Vue 2)
- Multiple v-model on components
- Suspense
component that renders fallback content instead of your component until a condition is met such as a loaded backend API - Portal
allows you to display blocks of code anywhere in the app (modals, notifications, etc) - Provide/Inject
Vue Composition API
In Vue 2, we know about the Option API model that we have used frequently in creating Vue components. Then what is the Composition API? In short, all the reactive data, computed, watch, methods we use in the Option API are now wrapped inside a setup()
function..
The Composition API is an alternative way of writing the logic behind our components. And it was introduced because we might face two main limitations or issues when building bigger Vue apps. In smaller and medium-sized apps, we will very likely never face those issues, but in bigger apps, we might face them. One of the issue is that the code that belongs together logically is actually split up across multiple options, data, methods, computed. Second issue is that re-using logic across components could be tricky and cumbersome.
ref
import { ref } from 'vue'
ref
is used to create reactive properties such as to keep track of user input, or to create reactive variables or objects. ref
is a wrapper object where the value of our property is stored in another property called .value
. So, if we want to call our properties we should access it by typing propertyName.value
ref vs reactive
Apart from ref
, we can also use reactive()
to create a reactive property. We can use ref
when the variables we create are in the form of strings, numbers, Boolean, etc. When dealing with objects, I recommend using reactive()
. If we use ref
, it will wrap our value inside the object .value
. reactive()
doesn’t wrap our object with values so we can immediately access it.
watchers
In Vue 2 we can only define one dependency if we use optional API, now we can define multiple watchers which return array. Here is the differences in how we define watchers in Vue 2 and Vue 3
Props with composition API
In Vue Option API, we could access props by typing this.propsName
. But in Vue Composition API, we use props as a parameter inside the setup()
method.
Emitting Custom Events using Context
There are cases when we need to emit data from child to parent. In Vue 2 we are familiar with using this.$emit(‘emitName’, data)
. But we can’t do this method in Vue 3, since we don’t have access to this
in setup()
. Thus, to emit events, we need to use a context object.
Context is the second parameter of setup()
. When we try to see the contents of the context by doing console.log()
there are 3 properties, namely attrs
, emit
, and slots
. attrs
are any fall through attributes you might have. slots
gives you access top any slot data you might have in your component. emit
is a function you can call to emit a custom event
Provide/Inject
We can use props when passing data from parent to child. Of course, it will be very easy if we pass data to a child that is only one level below the parent. But what if the component structure is like this?
We could either make child 1 receive props from the parent then pass the props data to child 2 or use state management. The first option seems to be inefficient since we add props in child 1 just to pass the data to the next children component — child 1 has nothing to do with the data. But there is another approach offered by Vue, namely by using Provide / Inject.
Provide / Inject is a capability where parent components can serve as dependency provider for all its children, regardless how deep the component hierarchy is.
It actually possible if you want to update the ref value in the child component but it is not recommended. You should always change injected values in the place where you provide them
To add reactivity between provided and injected values, we can use a ref or reactive when providing a value.
To ensure the data passed through cannot be mutated by the injected component, we can use readonly()
when passing the data.
provide ('name', readonly(name))
Lifecycle Hooks
In Vue 2, we have, beforeCreate
and created
in the options API, and we could simply add those methods to our config object. How does this look like with the composition API? With the composition API, we don’t add those lifecycle hooks to our component config object anymore, instead we have functions which we can import from vue, which you can call inside of the setup()
method. But for beforeCreate
and created
there actually is no equivalent, because these hooks are not needed here, because the setup()
method basically runs at the same time beforeCreate
and created
ran in the past. So setup()
replaces these hooks. Whatever you would have done in created
or beforeCreate
should be done in the setup()
method itself. Now for beforeMount
and mounted
, it’s different. There we have the onBeforeMount
and onMounted
functions which can be imported from vue. For beforeUpdate
and updated
it’s similar and the same for beforeUnmount
and unmounted
.
Vuex 4.0
Vue 3 also changed the way we access the store since we don’t have access to this.$store
in setup hook. To access the store within the setup
hook, you can call the useStore
function. This is the equivalent of retrieving this.$store
within a component using the Option API.
To access the state and getters, we have to create computed
references to retain reactivity, and to access mutation and action we could just simply use the commit()
and dispatch()
like we used to do in Vue 2.
We usually use mapGetters
and mapActions
in our components with the Options API. In Composition API we could use vuex-composition-helper by installing this package
First we import the useGetters
and useActions
, and then we create a constant which the name is the getter/action name — using object destructuring, and the last we expose the constant into our template. This way is easier to use when we are dealing with modules. So for example, if the incrementCounter
is inside the counter modules, we could write it like this
const { incrementCounter } =
useActions('counter',['incrementCounter'])
Summary
Here are the brief differences between Option API and Composition API
We no longer use data()
, instead we use ref()
and reactive()
to create reactive properties. We don’t define methods but create and regular javascript function inside the setup()
hook and then expose it to the template. We could have multiple watchers that return an array, and a lot more. You can find the complete documentation about Vue 3 and Vue Composition API here.