Props
Esta página supone que ya has leído los Fundamentos de los Componentes. Léelo primero si eres nuevo en el tema de componentes.
Declaración de Props
Los componentes de Vue requieren una declaración explícita de props para que Vue sepa qué props externos pasados al componente deben ser tratados como atributos fallthrough (que se discutirán en su sección dedicada).
En los SFC que utilizan <script setup>
, las props pueden declararse utilizando la macro defineProps()
:
vue
<script setup>
const props = defineProps(['foo'])
console.log(props.foo)
</script>
En los componentes que no son <script setup>
, las props se declaran utilizando la opción props
:
js
export default {
props: ['foo'],
setup(props) {
// setup() recibe props como primer argumento.
console.log(props.foo)
}
}
Observa que el argumento pasado a defineProps()
es el mismo que el valor proporcionado a las opciones props
: las mismas props de la options API son compatibles con los dos estilos de declaración.
Además de declarar las props utilizando un array de strings, también podemos utilizar la sintaxis de objetos:
js
// en <script setup>
defineProps({
title: String,
likes: Number
})
js
// sin <script setup>
export default {
props: {
title: String,
likes: Number
}
}
Para cada propiedad en la sintaxis de la declaración del objeto, la key es el nombre de la prop, mientras que el value debe ser la función constructora del tipo esperado.
Esto no sólo documenta tu componente, sino que también advertirá a otros desarrolladores que utilicen tu componente en la consola del navegador si pasan el tipo incorrecto. Discutiremos más detalles sobre validación de prop más adelante en esta página.
Si estás usando TypeScript con <script setup>
, también es posible declarar props usando anotaciones de tipo puro:
vue
<script setup lang="ts">
defineProps<{
title?: string
likes?: number
}>()
</script>
Más detalles: Escritura de las Props de Componentes
Detalles del Pase del Prop
Nomenclatura de las Props
Declaramos los nombres largos de las props usando camelCase porque esto evita tener que usar comillas cuando se usan como claves de propiedades, y nos permite referenciarlas directamente en las expresiones de las plantillas porque son identificadores válidos de JavaScript:
js
defineProps({
greetingMessage: String
})
template
<span>{{ greetingMessage }}</span>
Técnicamente, también puedes usar camelCase al pasar props a un componente hijo (excepto en plantillas en el DOM). Sin embargo, la convención es usar kebab-case en todos los casos para alinearse con los atributos HTML:
template
<MyComponent greeting-message="hola" />
Utilizamos PascalCase para las etiquetas de los componentes cuando es posible porque mejora la legibilidad de la plantilla al diferenciar los componentes Vue de los elementos nativos. Sin embargo, no hay tanto beneficio práctico en el uso de camelCase al pasar props, por lo que elegimos seguir las convenciones de cada lenguaje.
Props Estáticas vs. Dinámicas
Hasta ahora, has visto pasar props como valores estáticos, como en
template
<BlogPost title="Mi viaje con Vue" />
También has visto props asignadas dinámicamente con v-bind
o su atajo :
, como en:
template
<!-- Asignar dinámicamente el valor de una variable -->
<BlogPost :title="post.title" />
<!-- Asignar dinámicamente el valor de una expresión compleja -->
<BlogPost :title="post.title + ' por ' + post.author.name" />
Pasando Diferentes Tipos de Valores
En los dos ejemplos anteriores, resulta que pasamos valores de tipo string, pero se puede pasar cualquier tipo de valor a una prop.
Número
template
<!-- Aunque `42` es estático, necesitamos v-bind para decirle a Vue que -->
<!-- se trata de una expresión JavaScript en lugar de un string. -->
<BlogPost :likes="42" />
<!-- Asignar dinámicamente al valor de una variable. -->
<BlogPost :likes="post.likes" />
Booleano
template
<!-- Incluir la prop sin valor implicará ``true``. -->
<BlogPost is-published />
<!-- Aunque `false` es estático, necesitamos v-bind para decirle a Vue que -->
<!-- se trata de una expresión JavaScript en lugar de un string. -->
<BlogPost :is-published="false" />
<!-- Asignar dinámicamente al valor de una variable. -->
<BlogPost :is-published="post.isPublished" />
Array
template
<!-- Aunque el array es estático, necesitamos v-bind para decirle a Vue que -->
<!-- se trata de una expresión JavaScript en lugar de un string. -->
<BlogPost :comment-ids="[234, 266, 273]" />
<!-- Asignar dinámicamente al valor de una variable. -->
<BlogPost :comment-ids="post.commentIds" />
Objeto
template
<!-- Aunque el objeto es estático, necesitamos v-bind para decirle a Vue que -->
<!-- se trata de una expresión JavaScript en lugar de un string. -->
<BlogPost
:author="{
name: 'Veronica',
company: 'Veridian Dynamics'
}"
/>
<!-- Asignar dinámicamente al valor de una variable. -->
<BlogPost :author="post.author" />
Vinculación de Múltiples Propiedades Usando un Objeto
Si quieres pasar todas las propiedades de un objeto como props, puedes usar v-bind
sin argumento (v-bind
en lugar de :prop-name
). Por ejemplo, dado un objeto post
:
js
const post = {
id: 1,
title: 'Mi viaje con Vue'
}
La siguiente plantilla:
template
<BlogPost v-bind="post" />
será equivalente a:
template
<BlogPost :id="post.id" :title="post.title" />
Flujo de Datos Unidireccional
Todos las props forman una vinculación unidireccional entre la propiedad del hijo y la del padre: cuando la propiedad del padre se actualiza, fluye hacia el hijo, pero no al revés. Esto evita que los componentes hijos muten accidentalmente el estado del padre, lo que puede hacer que el flujo de datos de tu aplicación sea más difícil de entender.
Además, cada vez que se actualice el componente padre, todos los props del componente hijo se actualizarán con el último valor. Esto significa que no debes intentar mutar una prop dentro de un componente hijo. Si lo haces, Vue te avisará en la consola:
js
const props = defineProps(['foo'])
// ❌ aviso, ¡las props son solamente de lectura!
props.foo = 'bar'
Suele haber dos casos en los que es tentador mutar una prop:
La prop es usada para pasar un valor inicial; el componente hijo quiere utilizarla más adelante como una propiedad de datos local. En este caso, es mejor definir una propiedad de datos local que utilice la prop como valor inicial:
jsconst props = defineProps(['initialCounter']) // el contador sólo utiliza props.initialCounter como valor inicial; // está desconectado de futuras actualizaciones de la prop. const counter = ref(props.initialCounter)
La prop se pasa como un valor primario que necesita ser transformado. En este caso, es mejor definir una propiedad computada usando el valor de la prop:
jsconst props = defineProps(['size']) // propiedad computada que se auto-actualiza cuando la prop cambia const normalizedSize = computed(() => props.size.trim().toLowerCase())
Mutación de Props de Objetos / Arrays
Cuando se pasan objetos y arrays como props, mientras que el componente hijo no puede mutar el enlace de la prop, podrá mutar las propiedades anidadas del objeto o del array. Esto es porque en JavaScript los objetos y arrays se pasan por referencia, y es excesivamente costoso para Vue evitar tales mutaciones.
La desventaja principal de estas mutaciones es que permiten que el componente hijo afecte al estado del padre de una manera que no es obvia para el componente padre, haciendo potencialmente más difícil razonar sobre el flujo de datos en el futuro. La mejor práctica es evitar estas mutaciones a menos que el padre y el hijo estén estrechamente acoplados por diseño. La mayor parte de las veces, el hijo debería emitir un evento para que el padre realice la mutación.
Validación de Prop
Los componentes pueden especificar requisitos para sus props, como los tipos que ya has visto. Si un requerimiento no se cumple, Vue te avisará en la consola JavaScript del navegador. Esto es especialmente útil cuando se desarrolla un componente que va a ser utilizado por otros.
Con el fin de especificar validaciones de props, puedes proporcionar un objeto con requisitos de validación a la macro defineProps()
, en lugar de un array de strings. Por ejemplo:
js
defineProps({
// Comprobación básica de tipo
// (los valores `null` y `undefined` permitirán cualquier tipo)
propA: Number,
// Múltiples tipos posibles
propB: [String, Number],
// String requerido
propC: {
type: String,
required: true
},
// Número con valor por defecto
propD: {
type: Number,
default: 100
},
// Objeto con valor por defecto
propE: {
type: Object,
// Los valores por defecto de los objetos o arrays deben ser
// devueltos desde una función de fábrica. La función recibe las
// props originales recibidas por el componente como argumento.
default(rawProps) {
return { message: 'hola' }
}
},
// Función de validación personalizada
// Todas las props completas pasadas como 2do argumento en la versión 3.4 o superior
propF: {
validator(value, props) {
// El valor debe coincidir con una de estas cadenas
return ['éxito', 'advertencia', 'peligro'].includes(valor)
}
},
// Función con valor por defecto
propG: {
type: Function,
// A diferencia de los objetos o arrays por defecto, esta no es una
// función de fábrica; es una función que sirve como valor por defecto
default() {
return 'Default function'
}
}
})
TIP
El código dentro del argumento defineProps()
no puede acceder a otras variables declaradas en <script setup>
, porque toda la expresión se mueve a un ámbito de función externo cuando se compila.
Detalles adicionales:
Todas las props son opcionales por defecto, a menos que se especifique
required: true
.Cualquier props opcional ausente que no sea
Boolean
tendrá un valorundefined
.Las props ausentes
Boolean
se convertirán enfalse
. Deberías establecer un valordefault
para obtener el comportamiento deseado.Cuando se especifica un valor
default
, se utilizará si el valor resuelto de la props esundefined
; esto incluye tanto cuando la prop está ausente, como cuando se pasa un valorundefined
explícito.
Cuando falla la validación de la prop, Vue generará una advertencia en la consola (si se utiliza la compilación de desarrollo).
Cuando se utilizan declaraciones de props basadas en tipos , Vue hará todo lo posible para compilar las anotaciones de tipos en declaraciones de props equivalentes en tiempo de ejecución. Por ejemplo, defineProps<{ msg: string }>
se compilará en { msg: { type: String, required: true }}
.
Comprobaciones de Tipo en Tiempo de Ejecución
El tipo
puede ser uno de los siguientes constructores nativos:
String
Number
Boolean
Array
Object
Date
Function
Symbol
Además, type
también puede ser una clase o función constructora personalizada y la declaración será realizada con una comprobación instanceof
. Por ejemplo, dada la siguiente clase:
js
class Person {
constructor(firstName, lastName) {
this.firstName = firstName
this.lastName = lastName
}
}
Podrías usarlo como tipo de props:
js
defineProps({
author: Person
})
Vue utilizará instanceof Person
para validar si el valor de la prop author
es efectivamente una instancia de la clase Person
.
Asignación de Booleanos
Las props de tipo Boolean
tienen reglas especiales de asignación para imitar el comportamiento de los atributos booleanos nativos. Dado un <MyComponent>
con la siguiente declaración:
js
defineProps({
disabled: Boolean
})
El componente se puede utilizar así:
template
<!-- equivalente a pasar :disabled="true" -->
<MyComponent disabled />
<!-- equivalente a pasar :disabled="false" -->
<MyComponent />
Cuando se declara una prop para permitir múltiples tipos, las reglas de conversión para Boolean
también se aplicarán. Sin embargo, existe un límite cuando se permiten tanto String
como Boolean
: la regla de conversión de Boolean sólo se aplica si Boolean aparece antes de String:
js
// disabled se convertirá a true
defineProps({
disabled: [Boolean, Number]
})
// disabled se convertirá a true
defineProps({
disabled: [Boolean, String]
})
// disabled se convertirá a true
defineProps({
disabled: [Number, Boolean]
})
// disabled se interpretará como una cadena de texto vacía (disabled="")
defineProps({
disabled: [String, Boolean]
})