Skip to content

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.

Las props se declaran utilizando la opción props:

js
export default {
  props: ['foo'],
  created() {
    // las props van a estar expuestas a `this`.
    console.log(this.foo)
  }
}

Además de declarar las props utilizando un array de strings, también podemos utilizar la sintaxis de objetos:

js
export default {
  props: {
    title: String,
    likes: Number
  }
}
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
})
js
export default {
  props: {
    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
export default {
  data() {
    return {
      post: {
        id: 1,
        title: 'Mi viaje con Vue'
      }
    }
  }
}
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'
js
export default {
  props: ['foo'],
  created() {
    // ❌ aviso, ¡las props son solamente de lectura!
    this.foo = 'bar'
  }
}

Suele haber dos casos en los que es tentador mutar una prop:

  1. 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:

    js
    const 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)
    js
    export default {
      props: ['initialCounter'],
      data() {
        return {
          // el contador sólo utiliza props.initialCounter como valor inicial;
          // está desconectado de futuras actualizaciones de la prop.
          counter: this.initialCounter
        }
      }
    }
  2. 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:

    js
    const props = defineProps(['size'])
    
    // propiedad computada que se auto-actualiza cuando la prop cambia
    const normalizedSize = computed(() => props.size.trim().toLowerCase())
    js
    export default {
      props: ['size'],
      computed: {
        // propiedad computada que se auto-actualiza cuando la prop cambia
        normalizedSize() {
          return this.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()props option, 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.

js
export default {
  props: {
    // 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'
      }
    }
  }
}

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 valor undefined.

  • Las props ausentes Boolean se convertirán en false. Deberías establecer un valor default para obtener el comportamiento deseado.

  • Cuando se especifica un valor default, se utilizará si el valor resuelto de la props es undefined; esto incluye tanto cuando la prop está ausente, como cuando se pasa un valor undefined 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 }}.

Nota

Ten en cuenta que las props se validan antes de que se cree una instancia del componente, por lo que las propiedades de la instancia (por ejemplo, data, computed, etc.) no estarán disponibles dentro de las funciones default o validator.

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
})
js
export default {
  props: {
    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
})
js
export default {
  props: {
    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]
})
js
// disabled se convertirá a true
export default {
  props: {
    disabled: [Boolean, Number]
  }
}

// disabled se convertirá a true
export default {
  props: {
    disabled: [Boolean, String]
  }
}

// disabled se convertirá a true
export default {
  props: {
    disabled: [Number, Boolean]
  }
}

// disabled se interpretará como una cadena de texto vacía (disabled="")
export default {
  props: {
    disabled: [String, Boolean]
  }
}
Props has loaded