Vue introduces Composition API (Function-based API) as an addition to current Options-based API. The API will be released with Vue 3, but now you can try it with Vue 3 Composition API added to your Vue 2 app. In this tutorial, we’re gonna show you:
- New Vue Composition API overview and comparison with classic Vue Options-based API
- Examples to implement a Vue Component with new API: props, data, watchers, lifecycle hooks
- Example to take advantage of new Vue 3 Composition API (function-based API): split Topics into Functions
Related Posts:
– Vue v-slot tutorial with examples
– Vue 3 CRUD example with Axios & Vue Router
– Vue 3 Authentication with JWT, Vuex, Axios and Vue Router
Contents
What’s changed when Vue Composition API is added?
Everything will still work as it works before. Almost things don’t change:
- CLI
- Template syntax
- Object format
- Reactivity system
- Concepts of computed properties, watchers & component lifecycle
- SFC format
- Progressive nature of Vue framework
Options-based API vs Composition API
The difference of current Options-based API concept vs new Composition API (Function-based API) concept is the way we think of what a component contains:
- Options-based API: A component contains types of properties/methods/options.
- Composition API: A component encapsulates logical topics into functions.
Component options could become complicated to be organized and hard to maintain (monster component). A logical topic could involve properties in props
and data()
, some methods, a certain hook (beforeMount
/ mounted
), and a watcher in watch
. Hence one single topic will be fragmented across multiple options.
With Composition API, every function, which is a part of the big component, encapsulates all the code related to the logical topic (properties, methods, hooks, watchers). Now that smaller code (function) can be reused, and well-organized.
Use Vue 3 Composition API in current Vue 2 project
We can use new Vue 3 Composition API in our current Vue 2.x project by installing @vue/composition-api
module.
It is so easy, just run the command:
npm install @vue/composition-api
Or
yarn add @vue/composition-api
Then import it in main.js.
import Vue from 'vue';
import CompositionApi from '@vue/composition-api';
Vue.use(CompositionApi);
Vue setup() function
setup()
is the new component option that we will use new Vue Composition API to setup the logic (logical topics) of the component. If setup()
function becomes complex, we can easily split it into multiple functions with corresponding to logical topics.
When setup()
is called?
It is called after props resolution, when an instance of the component is created.
Now look at the Vue component with setup()
function:
const MyComponent = {
props: {
name: String
},
setup(props, context) {
console.log(props.name);
// context.attrs
// context.slots
// context.emit
// context.parent
// context.root
}
}
The function has 2 arguments:
– props
– context
context
has properties (attrs
, slots
, emit
, parent
, root
) that are corresponding to this.$attrs
, this.$slots
, this.$emit
, this.$parent
, this.$root
.
We can also destructure them with the latest values even after updates:
const MyComponent = {
setup(props, { attrs }) {
function onClick() {
attrs.foo // automatically update latest value
}
}
}
*Note:this
keyword is not available inside setup()
function.
So this.$emit
cannot be used like this:
setup() {
function onClick() {
this.$emit // not available
}
}
this.$refs
with new Composition API
To get a reference to an element or component instance in the template, we use ref
API so that setup()
can return reactive and mutable object for render context.
import { ref } from '@vue/composition-api'
const MyComponent = {
setup(props) {
const name = ref('bezkoder.com')
const appendName = () => {
name.value = `hello ${props.name}`
}
return {
name,
appendName
}
},
template: `<div @click="appendName">{{ name }}</div>`
}
ref
automatically unwraps to the inner value, so we don’t need to append .value
in the template: {{ name }}
is enough, not {{ name.value }}
.
Vue Composition API examples
In this section, we’re gonna show you examples that use new API along with old Vue2 options-based API syntax.
Vue Composition API Computed Values
Vue2 options-based API syntax:
export default {
props: {
title: String
},
computed: {
vTitle() {
return '-' + this.title + '-';
},
itemsQuantity() {
return this.items.length;
}
},
data() {
return {
items: ['This', 'is'],
};
},
}
Vue 3 Composition API syntax:
import { ref, computed } from '@vue/composition-api';
export default {
props: {
title: String
},
setup(props) {
const vTitle = computed(() => '-' + props.title + '-');
const items = ref(['This', 'is']);
const itemsQuantity = computed(() => items.value.length);
return {
vTitle,
items,
itemsQuantity,
};
}
};
With new computed API, we can create a writable ref
object with get
and set
functions.
const count = ref(1)
const double = computed({
get: () => count.value * 2,
set: val => { count.value = val - 1 }
})
double.value = 3 // set: count.value = 3 - 1 = 2
console.log(count.value) // 2
console.log(double.value) // get: count.value * 2 = 4
Vue Composition API Watchers
This is how we use Vue2 options-based API syntax:
export default {
data() {
return {
items: ['This', 'is'],
append: ''
};
},
watch: {
items: {
handler: function(value, oldValue) {
this.append = '';
value.forEach(item => {
this.append += item + ' ';
});
},
immediate: true
}
},
}
Like watch
option, we can use new Vue watch
API to perform side effect everytime a state is changed.
The syntax is: watch(source, callback, options)
source
: could be a getter function, a value wrapper, or an array containing the two above types (in case of watching multiple sources)callback
: is the function like Vue2 watcherhandler
function, with 2 arguments:newVal
,oldVal
. Each argument could be an array (for watching multiple sources):[newVal1, newVal2, ... newValN]
,[oldVal1, oldVal2, ... oldValN]
options
(optional): is used for configuring watcher type containing:lazy
,deep
,flush
.
For more details about WatchOption
, please visit: api#watch.
import { ref, watch } from '@vue/composition-api';
export default {
setup(props) {
const items = ref(['This', 'is']);
const append = ref('');
watch(
// getter
() => items.value,
// callback
(items, oldItems) => {
append.value = '';
items.forEach(item => {
append.value += item + ' ';
});
},
// watch Options
{
lazy: false // immediate: true
}
)
return {
items,
append
};
}
};
In case we want to watch multiple sources:
watch([aRef, bRef], ([a, b], [prevA, prevB]) => {
/* ... */
})
We can also split the multiple sources watcher
into smaller watchers. This helps us organize our code and create watchers with distinct options:
watch(
// getter
() => items.value,
// callback
(items, oldItems) => {
append.value = '';
items.forEach(item => {
append.value += item + ' ';
});
},
// watch Options
{
lazy: false // immediate: true
}
)
watch(
// getter
() => todo.value.length,
// callback
(length, oldLength) => {
todoLength.value = length;
},
// watch Options
{
lazy: true // immediate: false
}
)
Vue Composition API Lifecycle Hooks
With Vue2, we implement Lifecycle Hooks functions by this way:
export default {
beforeMount() {
console.log('V2 beforeMount!')
},
mounted() {
console.log('V2 mounted!')
}
};
New Vue 3 Composition API has equivalent functions, we can use those with on
prefix inside setup()
function:
import { onBeforeMount, onMounted } from '@vue/composition-api';
export default {
setup() {
onBeforeMount(() => {
console.log('V3 beforeMount!');
})
onMounted(() => {
console.log('V3 mounted!');
})
}
};
You can see the mapping between Lifecycle Vue2 Options and Composition API in the following table:
Vue2 Options-based API | Vue Composition API |
---|---|
beforeCreate | setup() |
created | setup() |
beforeMount | onBeforeMount |
mounted | onMounted |
beforeUpdate | onBeforeUpdate |
updated | onUpdated |
beforeDestroy | onBeforeUnmount |
destroyed | onUnmounted |
errorCaptured | onErrorCaptured |
Encapsulate logical Topics into Functions
Now we combine all code above into one Vue Component, you can see a complex component with multiple topics. We need to implement logic for title, for todo, for items:
import { ref, reactive, computed, watch, onBeforeMount, onMounted } from '@vue/composition-api';
export default {
props: {
title: String,
initInput: String
},
setup(props) {
const vTitle = computed(() => '-' + props.title + '-');
const todo = ref(props.initInput);
const todoLength = ref(0);
const items = ref(['This', 'is']);
const itemsQuantity = computed(() => items.value.length);
const append = ref('');
watch(
// getter
() => items.value,
// callback
(items, oldItems) => {
append.value = '';
items.forEach(item => {
append.value += item + ' ';
});
},
// watch Options
{
lazy: false // immediate: true
}
)
watch(
// getter
() => todo.value.length,
// callback
(length, oldLength) => {
todoLength.value = length;
},
// watch Options
{
lazy: false // immediate: true
}
)
const add = () => {
if (todo.value) {
items.value.push(todo.value);
todo.value = '';
}
};
const remove = index => {
items.value.splice(index, 1);
};
onBeforeMount(() => {
console.log('V3 beforeMount!');
})
onMounted(() => {
console.log('V3 mounted!');
})
return {
vTitle,
todo,
todoLength,
items,
itemsQuantity,
append,
add,
remove
};
}
};
It comes time to take advantage of Vue Composition API: split complex component into multiple functions with corresponding to logical topics:
import { ref, computed, watch, onBeforeMount, onMounted } from "@vue/composition-api";
function useTitle(props) {
const vTitle = computed(() => "-" + props.title + "-");
return {
vTitle
};
}
function useTodoLength(todo) {
const todoLength = ref(0);
watch(
// getter
() => todo.value.length,
// callback
(length, oldLength) => {
todoLength.value = length;
},
// watch Options
{
lazy: false // immediate: true
}
);
return {
todoLength
};
}
function useItems(todo) {
const items = ref(["This", "is"]);
const itemsQuantity = computed(() => items.value.length);
const append = ref("");
watch(
// getter
() => items.value,
// callback
(items, oldItems) => {
append.value = "";
items.forEach(item => {
append.value += item + " ";
});
},
// watch Options
{
lazy: false // immediate: true
}
);
const add = () => {
if (todo.value) {
items.value.push(todo.value);
todo.value = "";
}
};
const remove = index => {
items.value.splice(index, 1);
};
return {
items,
itemsQuantity,
append,
add,
remove
};
}
export default {
props: {
title: String,
initInput: String
},
setup(props) {
const todo = ref(props.initInput);
onBeforeMount(() => {
console.log("V3 beforeMount!");
});
onMounted(() => {
console.log("V3 mounted!");
});
return {
todo,
...useTitle(props),
...useTodoLength(todo),
...useItems(todo)
};
}
};
The results:
It works like a charm.
We can easily view each topic by its own function. Each topic has its own props, data, watchers, methods. Our component now only need to inject these functions and return them inside its setup()
function.
Fantastic!
Conclusion
Maybe you feel comfortable when using the old Vue options-based API, or maybe you don’t like to think of everything as functions but properties/methods with OOP mindset. The creators are developing Vue, make it better year after year and give us more options. Just try it and feel the good.
Happy learning! See you again!
Source Code
You can find the complete source code for this ‘Vue Composition Api example’ on Github.
Further Reading
– Vue 3 CRUD example with Axios & Vue Router
– Vue 3 Authentication with JWT, Vuex, Axios and Vue Router
Perfect tutorial
pefert tuto
Thanks! Vue 3 is coming and this is one of the best tutorials for trying its Composition API.
very helpful and easy to understand. Thank you.
Thank you so much for this new Vue 3 Feature tutorial with comparation with Vue 2.
Thank you!
Fantastic tutorial. Plenty of useful information about Vue Composition API here. I’m sending it to some friends. Thanks for your effort to make this Vue tutorial!