A few days ago I remembered a cool feature that was part of the RFCs that made it into Vue 3 and the composition API final releases when Evan You twitted this:
So I decided to share it in case you also didn't catch up with this nice feature at the time it was announced.
What is <script setup>
?
First, let's talk about how we normally implement Single Files components (SFCs) when using the Composition API
<template>
<button class="btn" @click="onClick">{{label}}</button>
</template>
<script>
import { ref } from 'vue';
export default {
setup() {
const label = ref(`I'm a very dangerous button`);
function onClick() {
label.value = `Don't touch me`
}
return {
label,
onClick
}
}
}
</script>
<style>
.btn {
outline: none;
border: none;
background: #5EDCAE;
padding: 15px;
border-radius: 5px;
color: white;
font-weight:600;
}
</style>
Here we have a very dangerous button that is ready to kick some as** at the best Cobra Kai style.
Sorry, I really liked the GIF 😅. As you see in the code above we are using the setup method to define the label and a default function when the user clicks our component and we exporting them into the <template />
to be used.
Very often setup
is the only option being used when authoring components using the Composition API and yes, one of most often complaints about it is the need to repeat all the bindings that need to be exposed to the render context.
This is where <script setup />
comes to town, with this attribute a new compile step is included where the code runs in the context of the setup()
function of the component. Applying that to our Dangerous Button:
<template>
<button class="btn" @click="onClick">{{label}}</button>
</template>
<script setup>
import { ref } from 'vue';
export const label = ref(`I'm a very dangerous button`);
function onClick() {
label.value = `Don't touch me`
}
</script>
Looks nicer right? Of course, for such a small component, it's difficult to see the benefit of this, but when components get bigger and bigger, it's appreciated.
Using setup()
arguments
What happens if we need to access the props
or the context
? Just add them as the value of the setup
attribute
<template>
<button class="btn" @click="onClick">{{label}}</button>
</template>
<script setup="props, {emit}">
import { ref } from 'vue';
export const label = ref(`I'm a very dangerous button`);
export function onClick() {
label.value = `Don't touch me`;
emit('No Mercy');
}
</script>
Declaring props or additional options
One caveat of <script setup>
is that removes the ability to declare other component options, like props
. This can be easily solved by treating the default export as additional options like this:
<script setup="props, {emit}">
import { ref } from 'vue';
export default {
props: {
color: String,
default: 'primary'
}
}
export const label = ref(`I'm a very dangerous button`);
export function onClick() {
label.value = `Don't touch me`;
emit('No Mercy');
}
export const buttonClass = computed(() => `btn-${props.color}`);
</script
Typescript
Would it work with Typescript? It should. To type setup arguments simply declare them
<script setup="props" lang="ts">
import { ref } from 'vue';
declare const props: {
color: String
}
export const label = ref(`I'm a very dangerous button`);
export function onClick() {
label.value = `Don't touch me`;
emit('No Mercy');
}
export const buttonClass = computed(() => `btn-${props.color}`);
</script
Before you go
It's important to highlight that this approach relies on the context of an SFC. script setup>
cannot be used with the src
attribute if the logic is moved to an external .js
or .ts
file.
For the sake of safety, make sure you click that 🦄 or ❤️ so we don't make our Dangerous button angrier than it currently is 😅. See ya in the comments!
This syntax makes every component so clear to read, I love it!