One of the key features of the Composition API is the setup function, which is used to initialize a component's state and behavior. In this topic, we'll discuss the purpose and usage of the setup function in the Composition API, as well as show how to interact with reactive data within the app.
Setup syntax
Vue 3 introduced the Composition API, which is a new way of organizing and composing logic within Vue components. One of the main aspects of this new approach is the setup function. It is the entry point for setting up a component's reactive state and logic. It came to replace Vue 2's data property, which is part of Options API. When compared to Options API, using setup function allows us to write more readable and maintainable code, use functional programming (no more this keyword), separate code by logic, and gather the benefits of TypeScript.
The basic example of the setup function looks like this:
setup(props, context) {
// component logic here
}The function takes in two parameters — props and context — we'll get to those a little later in the topic. Inside the setup function, we can use Vue's reactive and computed functions to create reactive data and computed properties. We can also define our component's methods using regular JavaScript functions.
Time to see a real practical example:
<script>
import { ref } from 'vue';
export default {
setup() {
const pet = ref('cat');
const togglePet = () => {
pet.value === 'cat' ? (pet.value = 'dog') : (pet.value = 'cat')
}
return {
pet,
togglePet
}
}
}
</script>One of the main benefits of using the Composition API and the setup function is the ability to return reactive references. This means that any variable or object you return from the setup function will be automatically made reactive, allowing Vue to track changes and trigger reactivity. To create a reactive reference, we can use the ref() function. In our example, we created a reactive pet variable and assigned it to "cat". We also declared a togglePet method that changes the state. Finally, we returned those to make them accessible within the template.
Here's the remaining code:
<template>
<div class="container">
<h2>Cats or dogs?</h2>
<div>{{ pet }}</div>
<button @click="togglePet">Toggle pet</button>
</div>
</template>
<style>
.container {
text-align: center;
}
button {
margin-top: 10px;
cursor: pointer;
}
</style>It's pretty straightforward — you just render the pet variable dynamically (using double curly braces) and link up the onclick event with the togglePet method.
Here's the result:
Alternative syntax
If you're familiar with Vue 2's Options API, you probably noticed that we used the same export default statement. Also, the return statement makes the script tag overloaded. Is there a way to make it cleaner? You bet. Take a look:
<script setup>
import { ref } from 'vue';
const pet = ref('cat');
const togglePet = () => {
pet.value === 'cat' ? (pet.value = 'dog') : (pet.value = 'cat')
}
</script>The setup on the top took care of everything for us and the code started to look cleaner. Also, this is a recommended way of writing code.
If you're looking to add TypeScript support, you can easily do it via lang="ts":
<script setup lang="ts">
// some code here
</script>Props
The setup function accepts two parameters: props and context. Props are values passed from a parent component to a child component. They can be used to customize the behavior or appearance of the child component. In the setup function, props are accessed through the props object.
To see a real example, create a new folder called "components" and move the code from the first section to a new file called Pet.vue. Then, modify the script tag like this:
<script>
import { ref } from 'vue';
export default {
props: {
text: String
},
setup(props) {
const pet = ref('cat');
const togglePet = () => {
pet.value === 'cat' ? (pet.value = 'dog') : (pet.value = 'cat')
}
console.log(props.text);
return {
pet,
togglePet,
}
}
}
</script>Before the setup function, we declared a props object. This is to let the component know that it should expect props with a certain name and type. Inside the setup, we console logged the value props.text. Currently, the console is empty, but no worries — you'll see some output later on. Instead of console logging, you can also use this value, pass it into some method, or render it in the template.
Props will lose reactivity if you destructure them, so it's always safer to reference them by props.propName syntax. However, if you still want to benefit from ES6 destructuring, you can use the toRef() or toRefs() utils.
The final step for us is to pass that text value from the parent component. Here's the App.vue file:
<script setup>
import Pet from './components/Pet.vue';
</script>
<template>
<Pet text="My cat's name is Molly" />
</template>We imported the Pet component, rendered it in the template and passed the string value via a text prop. Open up the console — hopefully, you see that output there. Success!
Context
The second parameter that the setup function accepts is context. Context contains information about the current component instance, such as its attrs, slots, and emit functions. It can be used to access or modify the component's state, emit events, or render dynamic content. Let's quickly go over each of those elements.
Attrs — attributes that were passed to a child component but not defined as props.
In our previous example, we passed only one prop — text. However, if you add another prop and don't declare it in the props object, you can still access it using context.attrs. We passed in a message prop and were able to access it in the child component:
export default {
props: {
text: String
},
setup(props, context) {
console.log(context.attrs.message);
}
}Slots — in simple words, these are custom content, usually in the form of a template that can be passed from a parent to a child component. If you're coming from React, it's pretty much likeprops.childrenfunctionality. Feel free to learn more info about this concept in the Vue.js docs. There's a fun playground example that helps internalize the concept pretty easily.
To access slots from a child component, you can write this:
export default {
setup(props, context) {
console.log(context.slots);
}
}Emit — a custom event that fires in the child component and is sent to a parent component. You can access emits like this:
export default {
setup(props, context) {
console.log(context.emit);
}
}
Context properties aren't reactive, so you can safely destructure them.
Conclusion
In this topic, you learned about the setup function and its parameters. You can use either full or short syntax: both are correct, though the latter is recommended. The setup function used in its full syntax accepts two parameters — props and context. They are useful when passing custom data and events between components.