Vue.js Composition Api is the new, optional way of defining component’s logic, that will be officially introduced in version 3. Right now in Vue.js 2, we can still use the new Api by installing Composition Api plugin.
New Api is inspired by React.js Hooks. Usage and basic concepts of these Apis will look similar, yet their behaviour is different.
Component definition
Currently in Vue.js 2 components are based on creating an object whose properties are responsible for: state management, methods, lifecycle methods, props etc. By using Composition Api we will use mostly two properties: props
for defining component’s props (already known from version 2) and setup
method, that will contain a whole logic definition of our component. setup
is a function that returns data that will be available in our component’s template.
export default {
props: {
// Props definition.
},
setup() {
// Component's logic.
// Returned data that will be used in a template.
return {}
}
}
Reading props
In Vue.js 2 props are accessed by referring to this of the component instance. Setup function from Composition API does not have own instance, yet props are passed as the first argument of the setup function.
export default {
props: {
user: {
type: Object,
required: true
}
},
setup(props) {
// We can access props by refering to first argument of setup function.
const userName = props.user.name;
return { userName };
}
}
Emitting events
Method for emitting events is defined on context
object, that is passed as the second argument to setup. We use it by calling context.emit
method and passing event name as a first argument and additional data as rest arguments.
export default {
setup(props, context) {
context.emit('counterUpdate', 10);
}
}
// Vue.js 2 equivalent
this.$emit('counterUpdate', 10);
State management
ref()
First of the function for managing state is called ref
, which we will be using to create a state that has a primitive type. Ref function returns an object with .value
property, since primitive values in JavaScript have no reference, so it would not be possible to track changes if they were not objects.
Example counter component logic using ref:
import { ref } from 'vue';
export default {
setup() {
const counter = ref(0);
const increment = () => counter.value++;
return { counter, increment };
}
}
It is worth mentioning that there is no need to access ref values with .value
property in Vue’s render template since it is being automatically unwrapped under the hood. We can simply use ref
value as it would not be an object (which is counter
in this case).
<template>
<button @click="increment">
// We are not accessing ref object
// by ".value" property in a template.
Counter is {{ counter }}
</button>
</template>
reactive()
The second function is called reactive
. This function accepts an object as a state value and it looks similarly to data method that is used in Vue.js 2 Api.
This example has equivalent behaviour like ref
’s one, but is using a reactive
function:
import { reactive } from 'vue';
export default {
setup() {
const state = reative({
counter: 0
});
const increment = () => state.counter++;
return { state, increment };
}
}
If you take a look, in comparison to ref
function, there is no need anymore to access state value by .value
property, since our state is already an object.
Reactive function has an additional feature, that when it receives
ref
as a value, it automatically unwraps it as it was primitive value. Same thing applies when we provide acomputed
value to a reactive, since computed returnsref
object.
import { ref, reactive } from 'vue';
export default {
setup() {
const counter = ref(0);
const state = reative({
// We have provided `counter` (ref object) to our state
counter
});
// We can access `counter` property without need of additional `.value` access,
// since `counter` object has been automatically unwrapped.
const increment = () => state.counter++;
return { state, increment };
}
}
computed()
A computed
function has the same behaviour and purpose as in Vue 2, yet it returns an object with .value
property as a result - it simply returns ref
object. Computed function accepts a callback as an argument that will be memoized until one of its dependencies (observed values) changes. In this particular case, our observed value is counter
.
import { ref, computed } from 'vue';
export default {
setup() {
const counter = ref(0);
const doubledCounter = computed(() => counter.value * 2);
return { counter, doubledCounter };
}
}
Side effects
watch()
watch
is a function that similarly to computed
will run if one of it’s dependencies changes, yet it does not return any value, instead it runs side effects like console log, or fetches new data when an observed state has changed.
import { ref, watch } from 'vue';
export default {
setup() {
const counter = ref(0);
// Everytime counter changes, it's value will be displayed in a console.
watch(() => console.log(counter.value));
return { counter };
}
}
Lifecycle hooks
Most of the lifecycle behaviour remains the same as in Vue 2, whereas lifecycle method names start with on prefix. Each method accepts a callback that will be called when specific lifecycle occurs.
Example usage of onMounted
lifecycle method:
import { onMounted } from 'vue';
export default {
setup() {
onMounted(() => {
console.log('Component has mounted');
});
return {};
}
}
Due to implementation reasons, in Composition Api, beforeCreate
and created
lifecycles have been moved to setup function and this is the place where they do occur.