¿Así que ya has avanzado mucho en tu camino ninja como Frontend Developer? ¡Felicidades!, pero es hora de hacer algo con esas increíbles habilidades que tienes 👀… ¡Juguemos un poco con Vue!
Espera… ¿Vue? ¡Pero yo no sé nada de Vue!
¡No te preocupes! Esto también es para ti, a lo largo de este blogpost te guiaré paso a paso para que podamos desarrollar este proyecto con Vue desde 0, y quién sabe, a lo mejor te animas a aprenderlo 😉.
Nuestro increíble proyecto
¡Hagamos un juego! Nuestro proyecto será un juego que interactúa con la API de Rick & Morty. Este nos mostrará una tarjetita misteriosa, y debajo de ella otras 3 tarjetitas, cada una con un personaje de la serie; nosotros tendremos que adivinar cuál de ellas eligió el juego. ¿Tienes ganas de empezar? ¡Vamos!

Preparándonos para empezar
Para trabajar como todos unos profesionales estaremos haciendo uso de la terminal para facilitarnos algunos procesos. En mi caso trabajaré desde Linux Ubuntu 20.04, pero tú puedes trabajar desde Windows o macOS, solo necesitas tener una terminal basada en UNIX.
Para el caso de macOS, su terminal es más que suficiente, por lo que con ella podrás trabajar sin problemas 💪. Para el caso de Windows, te sugiero trabajar con WSL, puedes aprender a cómo instalarlo en el Curso de Prework: Configuración de Entorno de Desarrollo en Windows 😉.
También puedes aprender más a fondo algunos de los comandos y trucos que usaremos en este blogpost en el Curso de Introducción a la Terminal y Línea de Comandos 👈👀.
Lo primero que haremos será crear la carpeta de nuestro proyecto y movernos a ella, puedes crearla y nombrarla a como prefieras:
mkdir rick-morty-game && cd rick-morty-game
Y como todo profesional, antes de escribir código, crearemos nuestro repositorio de git (te recomiendo empezar a trabajar con la rama main
):
git init

Trabajaremos este proyecto con Vue 3, y lo haremos por medio del CLI (sí, los profesionales trabajamos desde el CLI 😈). Para ello, vamos al sitio oficial de Vue CLI, estando ahí veremos algunos cuantos avisos sobre la actualización del Vue CLI. Lo más importante a considerar aquí es que necesitamos una versión de Node.js superior a la 8.9, aunque se recomienda la versión 10. Puedes revisar tu versión con el siguiente comando:
node --version
Si tienes una versión antigua es hora de actualizarla 😉.
También, veremos el comando que nos permitirá instalar Vue CLI usando NPM (o yarn como alternativa).

Simplemente copia el comando y ponlo en tu terminal para que se instale. En mi caso lo hago anteponiendo la palabra sudo
, ya que al ser una instalación global, esta requiere que NPM pueda modificar algunos directorios de administrador:

¡Genial! Ahora sí estamos listos para empezar. Para crear un nuevo proyecto de Vue debemos usar el comando vue create <folder-name>
, puedes crear el proyecto dentro de una nueva carpeta, o puedes usar la carpeta actual poniendo un punto en lugar del nombre de la carpeta, en este caso lo crearemos dentro de la carpeta que ya tenemos creada:
vue create .
Esto nos preguntará si queremos usar la carpeta actual para crear nuestro proyecto, por defecto viene seleccionada la opción “Y
”, damos enter para confirmar:

A continuación nos preguntará qué preset queremos usar. Vue te permite crear tus propios presets, pero como recién instalamos el CLI seleccionaremos el preset que dice Default (Vue 3 Preview) ([Vue 3] babel, eslint)
moviendo el selector con las flechas del teclado:

Esto empezará a generar toda nuestra estructura de carpetas y archivos ya configurados para empezar a trabajar con Vue 3 (sí, así de cómodo es el CLI 😉). Una vez terminado te dirá que todo fue creado satisfactoriamente dentro de nuestra carpeta y nos sugerirá un comando para ver nuestro proyecto de Vue: npm run serve
¡Ejecutémoslo!

¡Tenemos nuestra aplicación corriendo con Vue! Regresemos a la terminal, hay algunas cuantas cosas interesantes que quiero mostrarte:

Es importante aprender a leer lo que nos dice la terminal, en este caso nos dice que la aplicación está corriendo y nos muestra dos URL:
- Local: Esta es la URL con la que podemos acceder a nivel local, es decir, desde nuestra propia computadora.
- Network: Esta es la URL con la que podremos acceder a nuestra aplicación de Vue desde otros dispositivos conectados a la misma red.
También te dice que estás corriendo en modo de desarrollo, para compilar el proyecto en modo producción (optimizado) puedes ejecutar el comando npm run build
😉.
¡Creando nuestro juego!
¡Bien! Ya tenemos un proyecto con Vue creado, hora de programar 🤠. Nuestro componente principal está ubicado en src/App.vue
, este componente contiene el HTML, JavaScript y CSS solo de ese componente. Esta es la estructura básica de cada componente en Vue:
<template>
<div>
<p>El HTML del componente debe estar dentro de una etiqueta template, y esta solo debe tener una etiqueta hija.</p>
<p>Si pones más etiquetas hijas de template, en versiones anteriores a Vue 3 te dará un error 👀.</p>
</div>
</template>
<script>
// Podemos importar componentes para usarlos
import ComponentName from "./components/ComponentName"
// Exportamos la información por medio de un objeto JSON de ESTE componente
export default {
// El nombre de nuestro componente
name: "",
// Una lista en JSON de los componentes que este componente utiliza
components: {
ComponentName
},
// Podemos poner aún más funciones!
}
</script>
<style>
/* Aquí podemos poner reglas CSS, Sass, Less, etc. La etiqueta puede contener el atributo scoped para que los estilos se apliquen solo a este componente. */
body {
background: green;
}
</style>
Como puedes ver, un componente de Vue contiene prácticamente a nuestros 3 lenguajes principales de desarrollo web. No es obligatorio poner los 3, pero al menos uno debe estar presente.
¡Creemos un componente!
La estructura de nuestro juego es muy sencilla, ya que prácticamente solo tendremos un componente Card
. Este componente será cada una de nuestras tarjetitas, para ello, dentro de nuestra carpeta src/components
crearemos un componente llamado Card.vue
y dentro pondremos el siguiente código:
<template>
<div class="card">
<div class="front">
<h2>Rick Sánchez</h2>
<picture class="image-container">
<img src="https://i.pinimg.com/originals/ac/51/52/ac5152b9f7f50781b2b01e35463fc4e6.jpg" alt="Rick Sánchez">
</picture>
</div>
</div>
</template>
Ahora debemos exportar la información de este componente, para ello agregamos una etiqueta <script>
debajo de nuestro <template>
:
<script>
export default {
name: "Card"
}
</script>
Es algo muy sencillo, solo tendremos el nombre del personaje y su imagen. No profundizaremos en los estilos, pero puedes encontrarlos aquí, solo recuerda que debes ponerlos en una etiqueta <style scoped></style>
debajo de nuestro <script>
.
¡Muy bien! Ya que tenemos el componente listo (por ahora) nos toca importar este componente dentro de nuestro componente principal (tú recuerdas cuál es nuestro componente principal 👀). Borramos todo lo que tiene dicho componente, ya que no lo necesitaremos, y ponemos lo siguiente:
<template>
<main class="dashboard">
<div class="selected-card">
<Card />
</div>
<div class="options">
<Card />
<Card />
<Card />
</div>
</main>
</template>
Básicamente es el tablero, arriba tiene la tarjeta que el juego seleccionará y abajo nuestras 3 posibles opciones. También debemos exportar la información de nuestro componente principal, pero en este caso estamos usando un componente externo, así que también debemos importarlo, así debería quedar nuestro <script>
:
<script>
import Card from "./components/Card";
export default {
name: "App",
components: {
Card
}
}
</script>
Los estilos de este componente los puedes encontrar aquí 👈👀. Con esto listo, el juego debería verse algo así:

¡¿Qué?! ¿Ya tan rápido tenemos todo eso?..
¡Sí! Es la magia de usar componentes y los estilos hacen el resto 😉. Lo genial de esto es que si inspeccionas una tarjetita y le pones la clase closed
verás cómo esta tarjetita se cierra.

Conectándonos a la API de Rick & Morty
Muy bien, ya creamos nuestro primer componente, ahora falta conectarnos a la API para traernos a los personajes, para ello crearemos un archivo que haga ese trabajo por nosotros. Dentro de nuestra carpeta src
crearemos una carpeta llamada utils
, y dentro de la misma crearemos un archivo llamado api.js
, dentro pondremos el siguiente código:
const APIUrl = "https://rickandmortyapi.com/api/character";
// Obtiene un número aleatorio
function randomNumber(min, max) {
return Math.floor(Math.random() * (max - min) ) + min;
}
// Hace la solicitud a la API de Rick & Morty
async function fetchCharacter(url) {
return await fetch(url)
.then(response => response.json())
.then(data => data);
}
// Obtiene 3 personajes aleatoriamente consultando a la API
async function getCharacters() {
return [
await fetchCharacter(`${APIUrl}/${randomNumber(1, 669)}`),
await fetchCharacter(`${APIUrl}/${randomNumber(1, 669)}`),
await fetchCharacter(`${APIUrl}/${randomNumber(1, 669)}`),
];
}
export { getCharacters, randomNumber };
Es muy sencillo, tenemos tres funciones:
randomNumber
es una función que nos devolverá un número aleatorio entre un mínimo y un máximo.fetchCharacter
es una función que hará la solicitud del personaje hacia la API de Rick & Morty y nos devolverá un JSON con la información de dicho personaje.getCharacters
es una función que nos devolverá un array con 3 posiciones, cada una de ellas contendrá a un personaje que nos devuelva la API.
También guardamos en una constante el endpoint de la API que se encarga de proporcionarnos a los personajes. Dicho endpoint puede proveernos de hasta 699 personajes, por lo que mediante nuestra función randomNumber()
elegimos uno al azar.
Quiero que notes cómo la función fetchCharacter()
y getCharacters()
son asíncronas, esto es porque la solicitud la hacemos mediante la función fetch()
, que también es asíncrona, es por ello que nos apoyamos de las palabras async
y await
👀.
Al final exportamos a las funciones getCharacters()
y randomNumber()
, ya que usaremos ambas más adelante, pero estas funciones hay que importarlas en alguna parte. Las importaremos dentro de nuestro App.vue
, esto porque es el componente principal el que contiene a todas nuestras tarjetitas, así que es este componente quien tiene que decidir cuál tarjeta elegir. En el apartado de <script>
de dicho componente, antes de importar nuestro componente Card, importaremos nuestras funciones del archivo api.js
:
import { getCharacters, randomNumber } from "./utils/api";
¡Y ya estamos listos para usarlas! ¿Dónde las usaremos? Nos apoyaremos de los hooks de Vue, que básicamente son diferentes momentos que tiene Vue para renderizar nuestra aplicación, en especial del hook beforeCreate()
. ¿Recuerdas que exportamos la información de nuestro componente en formato JSON? Bien, ahí mismo, debajo de components
podemos añadir este hook, pero recuerda que usaremos una función asíncrona, así que añadiremos este hook con async
:
components: {
Card
},
async beforeCreate() {
console.log(await getCharacters());
}
Si ponemos un console.log
con nuestra función getCharacters()
verás que la API ya nos está dando información de los personajes, y si recargas la página verás que es información aleatoria:

Pasemos los datos de la API a nuestras tarjetas
¡Perfecto!, ya tenemos los datos de la API con los personajes que necesitamos de forma aleatoria, ahora solo falta que nuestro juego elija un personaje y muestre las opciones en pantalla. Para elegir a nuestro personaje lo haremos de manera aleatoria también, ¿recuerdas que la función getCharacters()
nos devuelve un array de 3 posiciones? Bien, usando la función randomNumber()
elegiremos al azar una de esas posiciones, lo que nos devolverá un personaje al azar:
async beforeCreate() {
const characters = await getCharacters();
const chosenCharacter = characters[ randomNumber(0, 2) ];
}
Como ya sabrás elegimos un número entre 0 y 2, ya que solo tenemos 3 posiciones dentro de nuestro array 👀.
Para asignar cada personaje a cada tarjetita haremos uso de los bindings de Vue. Con Vue tenemos una función llamada data()
, esta función debe retornar un objeto con cada una de las variables que usaremos en nuestro <template>
. Crearemos esta función data()
arriba de nuestra función beforeCreate()
:
data() {
return {
options: [],
chosenCharacter: {}
}
},
async beforeCreate() {
// Código que ya teníamos creado
}
Inicializamos ambas variables vacías, ya que serán rellenadas cuando nuestro beforeCreate
haga la consulta de los personajes, y para hacer esto debemos modificar dicha función para asignar los resultados a nuestras variables, por lo que al final quedaría así:
async beforeCreate() {
const characters = await getCharacters();
const chosenCharacter = characters[ randomNumber(0, 2) ];
this.options = characters;
this.chosenCharacter = chosenCharacter;
}
¡Ahora que tenemos nuestras variables listas podemos imprimirlas en nuestro <template>
!
Para poder asignar los personajes a cada tarjeta necesitamos pasarle los datos del personaje a nuestro componente Card
, ya que es este componente el que renderiza cada tarjetita, esto lo podemos hacer poniendo como atributo “dos puntos” y el nombre con el que queramos pasarle los datos. Por ejemplo, para pasarle los datos de nuestro personaje elegido al componente Card
nuestro HTML debería quedar así:
<div class="selected-card">
<Card :character="chosenCharacter" :closed="true" />
</div>
En este caso, le estamos pasando por medio de la propiedad character
los datos de nuestro chosenCharacter
(recuerda que este último es el personaje que nuestro juego eligió y que guardamos como variable para pasar al <template>
). El valor se pone directamente dentro de las comillas, gracias a esos dos puntos que pusimos antes del nombre de nuestra propiedad Vue sabe que se trata de una variable en lugar de un string literal. También estamos pasando una propiedad llamada closed
, esta propiedad nos dirá si la tarjeta debería renderizarse cerrada inicialmente o si debería estar abierta, como esta es la tarjeta que eligió el juego tiene que estar inicialmente cerrada.
Ahora solo nos queda recibir dichos atributos dentro de nuestro componente Card
, y para ello, dentro de la información que estamos exportando, podemos añadir una propiedad llamada props
quien será la que reciba dichos datos, por lo que podríamos añadirla debajo de su nombre. El export del componente Card
quedaría así:
<script>
export default {
name: "Card",
props: ["character", "closed"]
}
</script>
¡Ahora podemos usar las variables character
y closed
dentro del HTML de nuestro componente Card
! Recuerda que la variable character
es un objeto JSON, es el JSON que nos devolvió la API, por lo que podemos imprimir dicha información dentro de nuestro <template>
usando las dobles llaves {{ }}
. El HTML de nuestro componente Card
quedaría así:
<template>
<div class="card" :class="{ closed }">
<div class="front">
<h2>{{ character.name }}</h2>
<picture class="image-container">
<img :src="character.image" :alt="character.name">
</picture>
</div>
</div>
</template>
Antes de ver el resultado en pantalla te recomiendo comentar la sección “options” dentro del <template>
de tu App.vue
para evitar problemas con las otras cards.
Quiero que veas que aquí están pasando algunas cosas interesantes:
- Tenemos 2 atributos
class
en el div decard
, pero una de ellas tiene dos puntos. Para Vue, si le pones dos puntos a una clase, este lo tomará como un objeto JSON de clases el cual deberá tener condiciones. Lakey
del objeto JSON es la clase que quieres asignar, y elvalue
de ese objeto JSON es la condición que quieres poner para saber si agregar o no dicha clase, si una condición se cumple, entonces Vue agrega esa clase.
En este caso, lakey
(la clase que queremos agregar) esclosed
, pero la condición de esa clase está dada por una variable… ¡Que también se llamaclosed
! Es esta variable que recibimos en nuestroprops
, y si recuerdas, es una variable booleana, por lo que la magia de ECMAScript 6+ nos permite acortar este JSON. Pero si quisiéramos hacerlo de la forma larga, debería quedar así:
<div class="card" :class="{ closed: closed }">
💡 El primer closed es la clase que quiero poner, el segundo closed es la condición, que en este caso es un booleano que viene desde nuestro props.
</div>
- En el
<h2>
estamos pintando el nombre usando las{{ }}
, pero en el<img>
estamos pintando los datos usando la nomenclatura de “dos puntos”, esto es por lo que ya te expliqué hace unos párrafos ☝👀.
¡Nuestra tarjeta elegida ya tiene a su personaje! Ahora solo nos falta pintar las posibles opciones dentro de nuestro App.vue
. Estas opciones son un array, recuerda que cuando asignamos las variables que pasarán a nuestro HTML, para la variable options
asignamos el arreglo de personajes que nos devolvió la función getCharacters()
. Para recorrer un array dentro de Vue usamos una directiva llamada v-for
, por lo que el apartado de nuestras opciones debería quedar así:
<div class="options">
<Card v-for="option in options" :key="option.id" :character="option" :closed="false" />
</div>
Aquí sucedieron algunas cosas interesantes:
- Eliminamos 2 cards, ya que solo eran para ver nuestro maquetado.
- El
v-for
es prácticamente unfor...of
de JavaScript, es decir, recorremos cada personaje y por cada una de ellos lo gurdamos dentro de una variableoption
. - Tenemos un atributo
:key
, este atributo lo necesita Vue para identificar a cada elemento delv-for
, la key que le pasamos es elid
del personaje que nos devuelve la API. - Por último, recordemos que nuestro componente
Card
recibe un atributo llamadocharacter
, como estamos recorriendo el array de personajes le podemos pasar directamente todo el personaje, además, estamos mandando el atributo:closed
comofalse
porque queremos que estás tarjetas sí aparezcan abiertas inicialmente.
Hasta ahora nuestro juego ya debería estar casi terminado, por lo que si vamos al navegador deberíamos verlo así:

Recuerda que aquí nuestro juego ya nos está cargando a las 3 posibles opciones y ya eligió a una de ellas, ahora nos toca programar la interacción con el usuario para que elija una tarjeta 😎.
Para hacer esto, simplemente debemos agregarle un evento click a nuestras tarjetas, en Vue podemos agregar eventos usando una arroba (@
) seguido del nombre del evento directamente en nuestro componente. Sabiendo esto, a nuestro componente Card
que tenemos dentro del div con la clase options
le agregaremos el evento click, y cuando este evento suceda mandaremos a llamar a un método llamado validate()
al cual le pasaremos como parámetro el id del personaje que seleccionamos, este método lo crearemos en un momento:
<Card
v-for="option in options"
:key="option.id"
:character="option"
:closed="false"
@click="validate(option.id)"
/>
Para mejorar la legibilidad puse los atributos en forma de lista, ahora solo nos falta crear el método validate
. Para crear este método, debajo de nuestra función beforeCreate()
crearemos un nuevo objeto llamado methods
, y dentro de ese objeto podemos crear cuantos métodos queramos, para este caso solo crearemos el método validate
:
async beforeCreate() {
// Código que ya teníamos creado
},
methods: {
validate(chosenCharacter) {
// Aquí haremos la validación
}
}
¡Ya solo nos falta hacer la validación y girar la tarjetita cuando el usuario acierte! Recordemos que tenemos una variable dentro de nuestra función data()
que contiene el personaje que el juego eligió, y que por el parámetro de nuestro método validate
recibimos el id del personaje al que el usuario le hizo click… ya sabes qué sigue ¿verdad? 👀:
validate(chosenCharacter) {
if (chosenCharacter === this.chosenCharacter.id) {
this.isClosed = false;
alert("¡Lo has adivinado!");
}
else {
alert("Personaje incorrecto, inténtalo de nuevo");
}
}
Con esta pequeña validación podemos informarle al usuario si ha ganado o no, pero quiero que veas que puse this.isClosed = false;
, esto es porque también necesitamos girar la tarjeta cuando el usuario adivine, para ello, dentro de nuestra función data()
crearemos una nueva variable llamada isClosed
que empezará por defecto en true
, al final debería quedar así:
data() {
return {
options: [],
chosenCharacter: {},
isClosed: true
}
},
Y en nuestro componente Card
que está dentro del div
con la clase selected-card
simplemente cambiamos ese :closed="false"
que teníamos hardcodeado por :closed="isClosed"
, de esta forma cuando el usuario acierte y el valor de isClosed
cambie a true
la tarjetita se abrirá:
<Card :character="chosenCharacter" :closed="isClosed" />
¡Y listo! Con eso hemos terminado nuestro juego, como puedes ver solo agregamos una validación sencilla al final e hicimos que nuestra tarjetita se abra. Nuestro juego podría mejorar aún más, podríamos agregarle una opción para cargar nuevos personajes después de adivinar uno, ya tenemos la lógica para cargar el personaje, simplemente podríamos aislar esa lógica en un nuevo método, ¡o incluso podrías agregarle un score! ¿Te atreves a hacerlo? 👀.
Las posibilidades de mejorar este juego son infinitas, y Vue nos permite hacer todo esto de una forma muy fácil. A lo largo de este blogpost vimos muchas de las funcionalidades de Vue: componentes, comunicación entre componentes, two way data binding, consumo de API, eventos, hooks, métodos, etc. Pero la verdad es que Vue tiene todavía mucho más para dar, hay temas muy interesantes que complementan a esta librería y nos permite crear cosas aún más impresionantes como Vuex, Vue Router, Composition API o incluso JSX por si vienes de React, es por eso que Vue se autodenomina “El framework progresivo”.
Vue tiene mucho campo para explorar, es una librería muy cómoda y completa para trabajar además de que te permite lograr resultados poderosos. No olvides que puedes ver este proyecto funcionando en el siguiente enlace, y también puedes encontrar el repositorio de este proyecto en GitHub:
¡Genial! Has dado un paso más en tu camino ninja como Fronted Developer, pero ahora te toca a ti seguir aprendiendo. Si te gustaría aprender un poco más sobre este framework y profundizar más en él te invito a que tomes nuestra ruta de Frontend con Vue.js donde crearás proyectos todavía más increíbles 😉.
Y recuerda #NuncaParesDeAprender 😄.
Curso Básico de Vue.js 2