Cómo pasar datos entre componentes en Vue.js
Publicado: 2022-03-10Compartir datos entre componentes es una de las funciones principales de VueJS. Le permite diseñar un proyecto más modular, controlar los alcances de los datos y crear un flujo natural de datos en su aplicación.
A menos que esté creando toda su aplicación Vue en un componente (lo que no tendría ningún sentido), se encontrará con situaciones en las que necesitará compartir datos entre componentes.
Al final de este tutorial, conocerá tres formas de hacerlo.
- Usando accesorios para compartir datos de padres a hijos,
- Emitir eventos personalizados para compartir datos de niño a padre,
- Uso de Vuex para crear un estado compartido a nivel de aplicación.
Bien, ¡vamos a empezar!
Construyendo una aplicación con Nuxt
Con Spotify, tus amigos pueden ver lo que estás tocando. ¿Qué pasaría si el resto de Internet también pudiera experimentar su ritmo de algoritmo? Aprenda a crear su propia aplicación para compartir lo que está escuchando en Spotify usando Vue.js y Nuxt. Leer un artículo relacionado →
1. Uso de accesorios para compartir datos de padres a hijos
Los accesorios de VueJS son la forma más sencilla de compartir datos entre componentes. Los accesorios son atributos personalizados que podemos dar a un componente. Luego, en nuestra plantilla, podemos dar valores a esos atributos y, BAM, ¡estamos pasando datos de un componente principal a uno secundario!
Por ejemplo, supongamos que estamos trabajando en una página de perfil de usuario y queremos que un componente secundario acepte un accesorio de nombre de usuario. Necesitaremos dos componentes.
- El componente secundario que acepta la propiedad, lo llamaremos
AccountInfo.vue
. - El componente principal que pasa la propiedad, llamemos a esto
ProfilePage.vue
.
Dentro de AccountInfo.vue
, podemos declarar los accesorios que acepta usando la opción de accesorios. Entonces, dentro de las opciones del componente, hagamos que se vea como lo siguiente.
// AccountInfo.vue <template> <div id='account-info'> {{username}} </div> </template> <script> export default { props: ['username'] } </script>
Luego, para pasar los datos del padre ( ProfilePage.vue
), los pasamos como un atributo personalizado.
// ProfilePage.vue <account-info username='matt' />
Ahora, si cargamos nuestra página, podemos ver que nuestro componente AccountInfo
representa correctamente el valor pasado por su padre.
Al igual que cuando trabajamos con otras directivas de VueJS, podemos usar v-bind para pasar props de forma dinámica. Por ejemplo, supongamos que queremos configurar el nombre de usuario prop para que sea igual a una variable. Podemos lograr esto usando la forma abreviada de la directiva v-bind (o simplemente :
para abreviar). El código se vería un poco así:
<template> <div> <account-info :username="user.username" /> </div> </template> <script> import AccountInfo from "@/components/AccountInfo.vue"; export default { components: { AccountInfo }, data() { return { user: { username: 'matt' } } } } </script>
Esto significa que podemos cambiar nuestros datos y hacer que cualquier accesorio secundario que use ese valor también se actualice.
Consejo: siempre verifique sus accesorios
Si está buscando escribir un código Vue más claro, una técnica importante es verificar sus accesorios. En resumen, esto significa que debe especificar los requisitos para su accesorio (es decir, tipo, formato, etc.). Si uno de estos requisitos no se cumple (p. ej., si se pasa el accesorio de un tipo incorrecto), Vue imprimirá una advertencia.
Digamos que queremos que nuestra propiedad de nombre de usuario solo acepte cadenas. Tendríamos que modificar nuestro objeto props para que se vea así:
export default { props: { username: String } }
La verificación de accesorios es esencial cuando se trabaja en aplicaciones Vue a gran escala o cuando se diseñan complementos. Ayuda a garantizar que todos estén en la misma página y usen los accesorios de la manera en que fueron destinados.
Para obtener una lista completa de las verificaciones que podemos incluir en los accesorios, definitivamente recomendaría consultar la documentación oficial para una revisión en profundidad.
Sugerencia: siga las convenciones de nomenclatura de accesorios
De acuerdo con la guía de estilo de VueJS, la mejor manera de nombrar sus accesorios es usar camelCase
al declararlos en su script y kebab-case al hacer referencia a ellos en el código de la plantilla.
El razonamiento detrás de esto es bastante simple. En Javascript, camelCase
es la convención de nomenclatura estándar y en HTML, es kebab-case.
Entonces, Vue recomienda que nos ciñamos a las normas de cada idioma. Afortunadamente, Vue puede convertir automáticamente entre los dos estilos, por lo que no hay una configuración adicional para los desarrolladores.
// GOOD <account-info :my-username="user.username" /> props: { myUsername: String } // BAD <account-info :myUsername="user.username" /> props: { "my-username": String }
2. Emisión de eventos para compartir datos de niño a padre
Ahora que tenemos datos que pasan por la jerarquía, pasemos de otra manera: de un componente secundario a uno principal. No podemos usar accesorios, pero podemos usar eventos y oyentes personalizados.
Cada instancia de Vue puede llamar a un método .$emit(eventName)
que activa un evento. Entonces, podemos escuchar este evento de la misma manera que cualquier otro, usando la directiva v-on.
Creación de un evento personalizado
Construyamos nuestro ejemplo de perfil de usuario agregando un botón que cambia el nombre de usuario. Dentro de nuestro componente secundario ( AccountInfo.vue
), creemos el botón.
Luego, cuando se haga clic en este botón, emitiremos un evento llamado changeUsername
.
<template> <div id='account-info'> <button @click='changeUsername()'>Change Username</button> {{username}} </div> </template> <script> export default { props: { username: String }, methods: { changeUsername() { this.$emit('changeUsername') } } } </script>
Dentro del padre, manejamos este evento y cambiamos la variable user.username
. Como discutimos anteriormente, podemos escuchar eventos usando la directiva v-on o "@" para abreviar.
<template> <div> <account-info :username="user.username" @changeUsername="user.username = 'new name'"/> </div> </template>
Probémoslo. Debería ver que cuando hace clic en el botón, el nombre de usuario cambia a "nuevo nombre".
Sugerencia: los eventos personalizados pueden aceptar argumentos
El caso de uso más común para pasar argumentos a sus eventos es cuando desea que un componente secundario pueda establecer un valor específico para su accesorio. Nunca querrás editar directamente el valor de un accesorio desde el propio componente.
Sin embargo, afortunadamente podemos usar argumentos de paso con nuestros eventos personalizados para hacer que el componente principal cambie los valores.
Digamos que queremos modificar el evento changeUsername
para que podamos pasarle un valor.
El método $emit
toma un segundo parámetro opcional para los argumentos. Entonces, todo lo que hacemos es agregar nuestro nuevo valor de nombre de usuario después del nombre de nuestro evento.
this.$emit('changeUsername', 'mattmaribojoc')
Luego, en nuestro componente principal, podemos acceder a estos valores en línea usando una variable especial $event
, o podemos escribir un método de controlador que tome un parámetro.
<account-info :username="user.username" @changeUsername="user.username = $event"/> OR <account-info :username="user.username" @changeUsername="changeUsername($event)"/> export default { ... methods: { changeUsername (username) { this.user.username = username; } } }
3. Uso de Vuex para crear un estado compartido a nivel de aplicación
De acuerdo, sabemos cómo compartir datos entre padres e hijos, pero ¿qué pasa con otros componentes? ¿Tenemos que crear un sistema de jerarquía extremadamente complejo si queremos pasar datos?
Afortunadamente no. La maravillosa biblioteca de administración de estado de Vuex ha estado simplificando la vida de los desarrolladores durante años. En resumen, crea un almacén de datos centralizado al que pueden acceder todos los componentes.
En los métodos que usamos anteriormente (accesorios/eventos de emisión), cada componente tiene su propio estado de datos que luego compartimos entre los componentes. Sin embargo, Vuex nos permite extraer todos los datos compartidos en un solo estado al que cada componente puede acceder fácilmente. Este estado compartido se denomina almacén.
Probémoslo.
Debido a que Vuex está separado del código central de Vue, primero tendremos que instalarlo e importarlo a nuestro proyecto. Primero, tendremos que ejecutar npm install vuex --save
dentro de la CLI de nuestro proyecto.
Luego, cree una carpeta src/store con un archivo index.js que contenga el siguiente código.
// store/index.js import Vue from "vue"; import Vuex from "vuex"; Vue.use(Vuex); export default new Vuex.Store({ state: {}, getters: {}, mutations: {}, actions: {} });
Para incluir esto en nuestra instancia raíz de Vue, debemos importar nuestro archivo store/index.js y pasarlo a nuestro constructor de Vue.
// main.js import store from "./store"; new Vue({ store, ...
Acceder a los componentes internos de Vue Store
Dado que agregamos nuestra tienda Vuex en nuestra instancia raíz de Vue, se inyecta en todos los elementos secundarios de la raíz. Si queremos acceder a la tienda desde un componente, podemos hacerlo a través this.$store
.
Ahora, profundicemos en los detalles de cada una de las cuatro partes de una tienda Vuec.
1. Estado
El estado de Vuex es un objeto que contiene datos a nivel de aplicación. Todas las instancias de Vue podrán acceder a estos datos.
Para nuestra tienda, creemos un objeto de usuario que almacene más datos de perfil de usuario.
export default new Vuex.Store({ state: { user: { username: 'matt', fullName: 'Matt Maribojoc' } }, getters: {}, mutations: {}, actions: {} });
Podemos acceder a estos datos dentro de cualquier componente de instancia como este.
mounted () { console.log(this.$store.state.user.username); },
2. Compradores
Usamos captadores Vuex para devolver un valor modificado de datos de estado. Una buena manera de pensar en captadores es tratarlos como propiedades calculadas. Por ejemplo, los captadores, como las propiedades calculadas, almacenan en caché sus resultados y solo vuelven a evaluar cuando se modifica una dependencia.
Sobre la base de nuestra tienda anterior, digamos que queremos crear un método que devuelva el nombre de un usuario basado en el atributo de nombre completo.
getters: { firstName: state => { return state.user.fullName.split(' ')[0] } }
Las propiedades de getter de Vuex están disponibles para los componentes en el objeto store.getters
.
mounted () { console.log(this.$store.getters.firstName); }
Sugerencia: conozca los argumentos getter predeterminados
De forma predeterminada, los captadores de Vuex aceptan dos argumentos.
- state — el objeto de estado para nuestra aplicación;
- captadores: el objeto store.getters, lo que significa que podemos llamar a otros captadores en nuestra tienda.
Cada captador que declare requerirá el primer argumento de estado. Y dependiendo de cómo diseñes tu código, tus getters pueden hacer referencia entre sí usando el segundo argumento de 'getters'.
Hagamos un captador de apellidos que simplemente elimine nuestro valor de nombre de nuestra propiedad de estado de nombre completo. Este ejemplo requeriría tanto el estado como los objetos captadores.
lastName (state, getters) { return state.user.fullName.replace(getters.firstName, ''); }
Sugerencia: Pase argumentos personalizados a Vuex Getters
Otra característica interesante de los captadores es que podemos pasarles argumentos personalizados haciendo que nuestro captador devuelva un método.
prefixedName: (state, getters) => (prefix) => { return prefix + getters.lastName; } // in our component console.log(this.$store.getters.prefixedName("Mr."));
3. Mutaciones
Las mutaciones son la única forma de cambiar correctamente el valor del objeto de estado. Un detalle importante a tener en cuenta es que las mutaciones deben ser sincrónicas .
Al igual que los captadores, las mutaciones siempre aceptan la propiedad de estado de Vuex como primer argumento. También aceptan un argumento personalizado, llamado carga útil, como segundo argumento.
Por ejemplo, hagamos una mutación para cambiar el nombre de un usuario a un valor específico.
mutations: { changeName (state, payload) { state.user.fullName = payload } },
Luego, podemos llamar a este método desde nuestro componente usando el método store.commit
, con nuestro payload como segundo argumento.
this.$store.commit("changeName", "New Name");
La mayoría de las veces, querrá que su carga útil sea un objeto. Esto no solo significa que puede pasar varios argumentos a una mutación, sino que también hace que su código sea más legible debido a los nombres de propiedad en su objeto.
changeName (state, payload) { state.user.fullName = payload.newName }
Hay dos formas diferentes de llamar a las mutaciones con una carga útil.
- Puede tener el tipo de mutación como primer argumento y la carga útil como segundo.
- Puede declarar pasar un solo objeto, con una propiedad para el tipo y otra para la carga útil.
this.$store.commit("changeName", { newName: "New Name 1", }); // or this.$store.commit({ type: "changeName", newName: "New Name 2" });
No hay una diferencia real entre cómo funcionan los dos, por lo que depende totalmente de las preferencias personales. Recuerde que siempre es mejor ser consistente a lo largo de todo su proyecto, así que cualquiera que elija, ¡siga con él!
4. Acciones
En Vuex, las acciones son bastante similares a las mutaciones porque las usamos para cambiar el estado. Sin embargo, las acciones no cambian los valores en sí mismos. En cambio, las acciones cometen mutaciones.
Además, mientras que las mutaciones de Vuex tienen que ser sincrónicas, las acciones no. Usando acciones, podemos llamar a una mutación después de una llamada a la API, por ejemplo.
Mientras que la mayoría de los controladores de Vuex que hemos visto aceptan el estado como parámetro principal, las acciones aceptan un objeto de contexto. Este objeto de contexto nos permite acceder a las propiedades en nuestra tienda Vuex (por ejemplo, estado, confirmación, captadores).
Aquí hay un ejemplo de una acción Vuex que espera dos segundos y luego confirma la mutación changeName
.
actions: { changeName (context, payload) { setTimeout(() => { context.commit("changeName", payload); }, 2000); } }
Dentro de nuestros componentes, usamos el método store.dispatch para ejecutar nuestra función. Pasamos argumentos tal como lo hicimos con las mutaciones. Declaramos el tipo y pasamos cualquier argumento personalizado en el segundo argumento.
this.$store.dispatch("changeName", { newName: "New Name from Action" });
Terminando
Ahora, debe conocer tres formas diferentes de compartir datos entre componentes en VueJS: accesorios, eventos personalizados y una tienda Vuex.
Espero que este tutorial le haya ayudado a comprender mejor algunos métodos y mejores prácticas de Vue. ¡Déjame saber cómo los has implementado en tus proyectos!
Otras lecturas
Si está interesado en profundizar aún más en el lado técnico/capacidades de cada técnica, aquí hay algunos buenos lugares para comenzar.
- Sitio web de la guía oficial de Vuex
- Documentos de VueJS para accesorios y eventos personalizados
- “¿Qué diablos es Vuex? Una guía para principiantes sobre el almacén de datos de aplicaciones de Vue”, Anthony Gore, desarrolladores de Vue.js