El Setup Script de Vue 3 es la sintaxis recomendada si estas utilizando componentes de un solo archivo o composition API. Provee una estructura limpia y concisa que reduce significativamente el codigo redundate.
La idea principal detras del composition API es de exportar cualquier utileria que necesites utilizar (y reutilizarla). Por ejemplo, en el proximo codigo, ambos ref y computed son importados el core de Vue.
<script setup>
import { ref, computed } from 'vue'
const counter = ref(0)
const isNegative = computed(() => counter.value < 0)
</script>
Pero hay algunas utilerias que no necesitan ser importadas y que siempre estan disponibles para su uso en tus componentes, estas utilerias tienen el nombre de macros de compilacion y veremos 5 de estos.
1. defineProps
Esta es una macro esencial con la que probablemente familiarizaste durante tus primeras interacciones con el setup script. Te permite declarar accesorios con la misma sintaxis que en la API de opciones pero con soporte completo de inferencia de tipos TypeScript.
<script setup>
defineProps({
name: String,
age: {
type: Number,
default: 18,
},
})
</script>
Todas las mejores prácticas para declarar y validar una propiedad aún se aplican con esta sintaxis.
Dentro del template, todos los accesorios estarán disponibles automáticamente.
<script setup>
defineProps({
name: String
})
</script>
<template>
{{ name }}
</template>
Si necesitas utilizas las props adentro de tu script setup simplemente tienes que declarar una variale y asignarsela a defineProps.
<script setup>
const props = defineProps({
name: String
})
console.log(props.name)
</script>
2. defineEmits
La macro defineEmits es muy sencilla, nos permite documentar facilmente los eventos que nuestro component puede emitir.
Junto con TypeScript, esta es una herramienta muy poderosa, ya que nos ayuda a tener una claridad y una buena estructura en nuestros componentes.
<script setup>
const emit = defineEmits(['update:name', 'delete'])
</script>
En esta ejemplo utilizamos defineEmits
para declarar dos custom events: 'update:name'
y'delete'
. La variable emit
regresada por defineEmits
te permite activar estos eventos desde tu script o plantilla.
<script setup>
const emit = defineEmits(['delete'])
</script>
<template>
<button @click="emit('delete')">Delete</button>
</template>
Las emisiones también se pueden declarar usando sintaxis de tipo puro pasando un argumento de tipo literal a defineEmits
const emit = defineEmits<{
(e: 'change', id: number): void
(e: 'update', value: string): void
}>()
// Vue 3.3+: alternative, more succinct syntax
const emit = defineEmits<{
change: [id: number] // named tuple syntax
update: [value: string]
}>()
3. defineExpose
La macro defineExpose
es un poco confusa. Se suele utilizar para exponer tus metodos y propiedades del bloque de <script setup>
. Predeterminadamente, los componentes que utilizan script setup esta cerrados, lo que significa que sus enlaces internos no son expuestos a sus componentes padres.
Sin embargo, en algunos casos necestaras que ciertas propiedades y metodos sean accesibles al componente padre.
<script setup>
const message = 'Hello, world!'
const count = ref(0)
defineExpose({
message,
increment() {
count.value++
},
})
</script>
En este ejemplo, expusimos la variable message y el metodo increment hacia el componente padre. Estos pueden ser utilizados desde cualquier componente padre utilizar un template ref
<script setup>
import ChildComponent from "./ChildComponent.vue"
import { ref } from "vue"
//Deben de tener el mismo nombre que en template
const childComponentRef = ref(null)
</script>
<template>
<h1>Parent Component</h1>
<p>Message: {{childComponentRef?.message}}</p>
<p>
Increase counter in the child component:
<button @click="childComponentRef.increment">+1</button>
</p>
<hr>
<ChildComponent ref="childComponentRef"/>
</template>
De esta manera, el componente principal puede tener acceso al estado interno y a los métodos del componente secundario, lo que a veces resulta útil, especialmente cuando se desarrolla una biblioteca de terceros.
Pero ten en cuenta que este patrón podría provocar el acoplamiento de componentes. Para nuestro ejemplo, mover el estado del childComponent a un elemento componible probablemente sea una mejor opción.
4. defineOptions
El macro defineOptions
te ayuda a declarar opciones adicionales en tu <script setup>
.
Esto puede resultar útil cuando necesita especificar opciones que no se pueden expresar utilizando únicamente macros de configuración de script.
<script setup>
defineOptions({
name: "ComponentName",
owner: "Team A",
inheritAttrs: false
})
</script>
Nota: defineOptions
es introducido en Vue 3.3. En versiones previas, necesitabamos utilizar dos scripts separados
<!-- Syntax before Vue 3.3 -->
<script setup>
// Component logic goes here
</script>
<script>
export default {
name: "ComponentName",
owner: "Team A",
inheritAttrs: false
}
</script>
5. defineModel
Esta es otra adición de Vue 3.3 que tiene como objetivo simplificar el enlace de datos bidireccional dentro de la configuración del script, reducir el texto estándar y mejorar la experiencia de desarrollo.
Anteriormente para soporte el two-way binding con v-model, necesitabamos declarar un prop y un emit con su correspondiente evento update:propName
.
<!-- Syntax before Vue 3.3 -->
<script setup>
const props = defineProps(['modelValue'])
const emit = defineEmits(['update:modelValue'])
console.log(props.modelValue)
function onInput(e) {
emit('update:modelValue', e.target.value)
}
</script>
<template>
<input :value="modelValue" @input="onInput" />
</template>
Podemos hacer lo mismo con defineModel
. Esta macro registra automaticamente una prop y regresa un ref que puede ser directamente mutado.
<script setup>
const modelValue = defineModel()
console.log(modelValue.value)
</script>
<template>
<input v-model="modelValue" />
</template>
Conclusion
En conclusion, si bien la utilidad de estas macros varía, cada una de ellas es esencial al escribir código en Vue 3 y Script Setup. Puedes encontrar mas acerca de ellas en la documentacion oficial de Vue y algunas funciones adicionales en Vue Macros external library.