Tutorial de introducción a Vue 3

Vue

En este tutorial aprenderás a crear una aplicación desde cero usando Vue. Veremos primero los conceptos fundamentales de Vue, así como la anatomía de las aplicaciones creadas con este framework.

Seguidamente aprenderás a crear una aplicación CRUD paso a paso con Vue. Cuando finalice este tutorial, habrás aprendido a crear componentes, eventos, métodos, propiedades computadas y formularios, además de saber gestionar el estado de los componentes.

El número de librerías de JavaScript no para de aumentar. Sin embargo, cuando hablamos de librerías frontend, las que más destacan son sin duda React, Vue, Angular y Svelte. Del mismo modo que ya he creado un tutorial de introducción a React, ahora le toca a Vue, que compite directamente con React. Vue es un framework que todavía no ha llegado al nivel de popularidad de React. Sin embargo, crece a un ritmo tremendamente rápido, especialmente gracias al apoyo de frameworks como Laravel.

Contenidos

Qué es Vue

Vue.js, también conocido como Vue a secas, es un framework open source de JavaScript que se encarga de proporcionar las herramientas necesarias para creación de las vistas en una típica arquitectura MVC. Este framework fue creado por Evan You, un desarrollador independiente amante del código open source.

Vue es una de las librerías estrella actualmente, usada por miles de desarrolladores. Su popularidad implica que su demanda esté en alza, por lo no es difícil encontrar ofertas de empleo que requieran conocimientos de Vue. Además, es una de las librerías más populares de GitHub.

Introducción

Existen una serie de requisitos que deberías tener en cuenta antes de continuar con este tutorial. Para empezar, deberías saber programar con JavaScript. Además, también deberías saber crear páginas con HTML y CSS. Si no dispones de estos conocimientos, consulta primero los tutoriales que ves a continuación, ya que te servirán de gran ayuda durante el proceso de aprendizaje de Vue:

También deberías tener ciertos conocimientos básicos de la línea de comandos. Aunque no es imprescindible, tampoco estaría mal que sepas gestionar el código de tus proyectos con Git. Aquí tienes un par de guías que te pueden resultar ser de ayuda:

Finalmente, como último requisito, también deberías tener tanto Node.js como npm instalados en tu sistema. Es algo que no te llevará más de cinco minutos.

Lo que vamos a hacer en este tutorial es crear una sencilla aplicación de gestión con Vue. La aplicación permitirá gestionar los datos de diferentes personas. Veremos cómo instalar Vue y cómo configurarlo. Seguidamente verás cuál es la estructura de un archivo Vue. Luego aprenderás a crear componentes, propiedades, métodos, eventos y diferentes tipos de sentencias de Vue.

Para crear la interfaz de la aplicación, usaremos tablas y formularios, agregando campos a estos últimos junto con las validaciones pertinentes. Por último, también aprenderás a desplegar una aplicación creada con Vue en GitHub pages.

Este es el resultado final de la aplicación que vas a crear.

Dicho esto comenzaremos con la instalación de Vue en tu sistema.

Instalación de Vue

Puedes instalar Vue de diversas formas. La opción más habitual consiste en instalar la herramienta de terminal de comandos de Vue, denominada Vue CLI, mediante la cual podrás inicializar proyectos con gran facilidad. Por otro lado, también puedes agregar el script de Vue a un archivo HTML, que es la opción que resultará más sencilla a aquellos que nunca hayan utilizado un framework JavaScript.

Primero veremos cómo agregar el script de Vue en un archivo HTML, algo que resultará muy familiar a los desarrolladores que estén acostumbrados a usar jQuery, Si ya sabes usar React, Svelte o cualquier otro framework JavaScript, puedes saltarte este primer método e instalar Vue desde la terminal de comandos.

Mediante un archivo HTML estático

Esta es la opción más sencilla, ya que bastará con incluir el script de Vue desde una CDN en la sección head un archivo HTML. Nuestra aplicación Vue estará incluida en un div al que identificaremos mediante el atributo id, cuyo valor será app. En el siguiente archivo HTML vemos cómo agregarlo:

<!DOCTYPE html>
<html lang="es">
  <head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width,initial-scale=1.0" />
    <meta name="description" content="Tu primera aplicación con Vue" />

    <script src="https://unpkg.com/vue@3.0.3/dist/vue.global.prod.js"></script>

    <title>Aplicación básica con Vue</title>
  </head>

  <body>
    <div id="app"></div>
  </body>
</html>

Tal y como ves, hemos incluido la versión 3.0.3 de Vue. Para incluir otras versiones, basta con que cambies la porción @3.0.3 de la URL y especifiques el número de versión que quieres agregar en su lugar. En caso de querer incluir una versión de desarrollo de Vue y no de producción, tendríamos que incluir este script en su lugar:

<script src="https://unpkg.com/vue@3.0.3/dist/vue.global.js"></script>

A continuación vamos a crear la aplicación más sencilla posible que puedes crear con Vue, que no es otra cosa que un Hola Mundo!. No, definitivamente no quiero más Hola Mundo!. En su lugar, desde ahora las aplicaciones más sencillas serán las aplicaciones Hail to the King, Baby! (enlace motivacional).

Para ello agregaremos un script justo antes del cierre de la etiqueta body. En el script crearemos una nueva instancia de una aplicación Vue ejecutando la sentencia app = Vue.createApp({ ... }), que recibirá un objeto como parámetro:

const app = Vue.createApp({
  data() {
    return {
      saludo: 'Hail to the King, Baby!'
    }
  },
});

El objeto que hemos pasado al constructor de la clase Vue incluye el método data, que debe devolver aquellos datos con los que queramos inicializar nuestra aplicación. En nuestro caso será únicamente la variable saludo.

Para inicializar la aplicación Vue que hemos creado, debemos usar el método mount, que recibirá el selector CSS del elemento en el que queremos renderizar la aplicación, que en nuestro caso es el div #app:

app.mount('#app');

Seguidamente, renderizaremos la propiedad saludo en el interior del div app usando la sintaxis {{ saludo }}:

<div id="app">{{ saludo }}</div>

Este sería el código completo de esta primera aplicación introductoria:

<html lang="es">
  <head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width,initial-scale=1.0" />
    <meta name="description" content="Tu primera aplicación con Vue" />
    
    <script src="https://unpkg.com/vue@3.0.3/dist/vue.global.prod.js"></script>

    <title>Aplicación básica con Vue</title>
  </head>

  <body>
    <div id="app">{{ saludo }}</div>

    <script>
      const app = Vue.createApp({
        data() {
	  return {
            saludo: 'Hail to the King, Baby!'
	  }
        },
      });

	  app.mount('#app');
    </script>
  </body>
</html>

Tal y como ves, hemos usado únicamente JavaScript junto con algunos cambios de sintaxis que provocarían errores. Sin embargo, cuando ejecutas el código, puedes comprobar que el resultado es el siguiente:

No existen errores porque el código Vue se traduce a código JavaScript mediante un compilador llamado Babel aunque no lo percibas. Esto ocurrirá cada vez que se ejecute la aplicación. Sin embargo, no es habitual agregar Vue de esta forma, por lo que la compilación, tal y como verás, solamente se suele realizar cuando desarrollas la aplicación y no cuando la utilizan tus usuarios en producción.

Es totalmente normal pensar que todo esto es excesivamente complicado cuando con HTML y JavaScript harías esto en un santiamén. No te preocupes; yo pensaba exactamente lo mismo. Cuando finalices este tutorial habrás cambiado de idea 👍.

Mediante la herramienta Vue CLI

No es habitual incluir Vue desde una CDN por el inconveniente de que el código Vue se debe compilar cada vez que un usuario carga la aplicación. Lo que se suele hacer es crear un proyecto usando al herramienta Vue CLI y compilar (transpilar) el código Vue en JavaScript antes de subirlo a tu servidor, de forma que los navegadores puedan entenderlo.

Vue CLI es una aplicación de terminal o línea de comandos que te ayudará a desarrollar aplicaciones Vue. Vamos a ver cómo instalarla globalmente, de modo que puedas crear aplicaciones en cualquier lugar de tu sistema. Para ello debes abrir una ventana de terminal y ejecutar uno de los comandos que ves a continuación, dependiendo del gestor de paquetes que utilices:

  • Para instalar Vue CLI mediante npm debes ejecutar el siguiente comando:
    npm i -g @vue/cli @vue/cli-service-global
  • Para instalar Vue CLI mediante yarn debes ejecutar el siguiente comando:
    yarn global add @vue/cli @vue/cli-service-global

Ahora que ya tenemos la aplicación Vue CLI instalada en nuestro sistema, ya podemos usar el comando vue.

Para crear una nueva aplicación, desplázate al directorio en el que quieres crear tu aplicación y ejecuta el siguiente comando para crear una aplicación cuyo nombre será tutorial-vue:

vue create tutorial-vue

Se te preguntará si quieres crear la aplicación con Vue 2 con las opciones por defecto, con Vue 3 con las opciones por defecto o si por el contrario prefieres seleccionar manualmente las opciones. Selecciona la opción Vue 3, que instalará Vue 3 junto con Babel y ESLint.

Alerta! Si usas Git Bash en Windows, has de saber que no es un terminal interactivo, por lo que tendrás que usar el comando winpty vue.cmd create tutorial-vue en lugar de vue create tutorial-vue.
Seguidamente se te preguntará si prefieres usar el gestor de paquetes Yarn o el gestor de paquetes NPM. En este tutorial usaremos NPM, aunque puedes seleccionar la opción que prefieras.

Tras unos segundos, la aplicación estará ya creada. Seguidamente, desplázate al directorio que se ha creado:

cd tutorial-vue

Luego ejecuta uno de los siguientes comandos para iniciar el servidor de desarrollo, que por defecto estará en el puerto 8080:

  • Para iniciar el servidor con npm ejecuta el comando serve:
    npm run serve
  • Para iniciar el servidor con yarn usa también el comando serve:
    yarn serve

Una vez creada la aplicación, abre tu navegador y accede a la URL http://localhost:8080/ para ver la aplicación, que incluye la siguiente página por defecto:

En el directorio que se ha creado verás que hay un directorio llamado public, en cuyo interior encontrarás el archivo index.html. Este es el directorio en el que se situarán los archivos ya compilados. El directorio en donde estará tu código será el directorio src, en donde encontrarás el archivo main.js, que es el punto de entrada de la aplicación.

En el archivo main.js importamos el método createApp y el código principal de la aplicación, localizado en el archivo App.vue. Para crear la aplicación sencillamente ejecutamos el método createApp(App). Para incluirla o montarla y que se renderice en un elemento HTML de nuestra página, que en este caso es el div #app, usamos el método mount('#app'):

import { createApp } from 'vue'
import App from './App.vue'

createApp(App).mount('#app')

Si accedes al archivo App.vue, verás que se importa un componente de ejemplo llamado HelloWord.vue, cuyo código está en otro archivo. Explicaremos lo que es un componente y cómo se estructuran los archivos .vue más adelante.

Entorno de desarrollo de Vue

Tras instalar Vue, ya podríamos comenzar a programar la aplicación. Sin embargo, vamos a configurar primero una serie de herramientas que nos serán de gran ayuda durante el proceso.

Resaltado de sintaxis en Vue

Puedes usar cualquier IDE, aunque tendrás que usar algún plugin de resaltado de sintaxis que además también pueda formatear el código. Aquí tienes el más utilizado para cada IDE:

En mi caso, utilizo VS Code con Vetur.

Instalación de las DevTools de Vue

Antes de continuar, es recomendable que instales las DevTools de Vue en tu navegador, que están disponibles tanto para Chrome como para Firefox. Se trata de una serie de herramientas que te permitirán inspeccionar el código de tu aplicación, sus componentes, sus métodos o su estado mientras la ejecutas. Dependiendo del navegador que uses, instala uno de los siguientes plugins:

Si usas Edge para programar, algo poco probable, pero posible, es recomendable que uses Chrome o Firefox por ahora para poder inspeccionar el código.

Alerta! Dependiendo de cuándo consultes este tutorial, puede que las DevTools de Vue no funcionen en Chrome. Si esto ocurre, usa esta versión en su lugar.

Incluye un framework CSS

El objetivo de este tutorial no es el de que aprendas a usar CSS, por lo que vamos a usar un framework. Agregaría  el framework Tailwind, que está en auge. Sin embargo, para que este tutorial resulte más sencillo a programadores noveles, usaremos Bootstrap. Si nunca has usado Bootstrap, puedes consultar el tutorial de introducción a Bootstrap.

Para incluir Bootstrap en el proyecto, abre el archivo public/index.html y agrega la siguiente etiqueta en la sección head:

<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" />

Estructura de un archivo Vue

Los archivos Vue se dividen en tres secciones muy diferenciadas. Por un lado tenemos la sección template, en donde agregaremos el código HTML de la aplicación. Por otro lado, tenemos la sección script, en donde tal y como su nombre indica agregaremos el código JavaScript. Finalmente, tenemos la sección style, en donde agregaremos el código CSS:

<template></template>

<script>
  export default {
    name: 'nombre-componente,
  }
</script>

<style scoped></style>

Seguramente estés habituado a separar el código HTML del código CSS y JavaScript. Además creerás que es una mala práctica unirlo todo. Sin embargo, las aplicaciones Vue se dividen en componentes reutilizables, por lo que en cada uno de ellos incluiremos únicamente el código HTML, JavaScript y CSS inherente a dicho componente. Esto evitará que tengamos que navegar por una gran cantidad de archivos y hará que las aplicaciones sean más fáciles de mantener.

Lo cierto, es que no es correcto llamar HTML al código de la sección template, puesto que se trata de código Vue. Similar, pero no igual.

La parte lógica y los datos del componente se agregan en la sección <script> ... </script>. La sentencia export permite exportar nuestros componentes, de forma que pueda incluirse en otros archivos.

En la sección <style> ... </style> se incluirá el código CSS del componente. Gracias al atributo scoped, el código CSS solamente afectará al componente actual.

Creación de componentes

Vamos a crear una aplicación para gestionar datos de personas, por lo que, antes de nada, vamos a crear un componente que muestre una lista de personas en el directorio src/components. Al archivo le llamaremos TablaPersonas.vue, ya que la convención en Vue es que los nombres de archivo estén en Pascal Case.

Crea un componente con Vue

A continuación agregaremos el siguiente código, en donde incluiremos una sencilla tabla HTML. Además, verás que incluimos el div <div id="tabla-personas"> antes de la tabla. El motivo es que en todos los componentes en Vue deben contener un único elemento como ráiz:

<template>
  <div id="tabla-personas">
    <table class="table">
      <thead>
        <tr>
          <th>Nombre</th>
          <th>Apellido</th>
          <th>Email</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <td>Jon</td>
          <td>Nieve</td>
          <td>jon@email.com</td>
        </tr>
        <tr>
          <td>Tyrion</td>
          <td>Lannister</td>
          <td>tyrion@email.com</td>
        </tr>
        <tr>
          <td>A</td>
          <td>Daenerys</td>
          <td>Targaryen</td>
          <td>daenerys@email.com</td>
        </tr>
      </tbody>
    </table>
  </div>
</template>

<script>
  export default {
    name: 'tabla-personas',
  }
</script>

<style scoped></style>

Aunque el nombre de archivo esté en Pascal Case, lo habitual en Vue es que el nombre del componente en el interior del archivo que lo contiene esté en Kebab Case, de modo que se respete la convención que se usa en HTML.

Agrega el componente a la aplicación

Una vez hemos creado y exportado el componente TablaPersonas, vamos a importarlo en el archivo App.vue. Para importar el archivo escribiremos la siguiente sentencia en la parte superior de la sección script del archivo App.vue:

import TablaPersonas from '@/components/TablaPersonas.vue'

Tal y como ves, podemos usar el carácter @ para referenciar al directorio src.

Seguidamente, vamos a agregar el componente TablaPersonas a la propiedad components, de modo que Vue sepa que puede usar este componente. Debes agregar todos los componentes que crees a esta propiedad.

Para renderizar el componente TablaPersonas, basta con agregar <tabla-personas />, en Kebab case, al código HTML. A continuación puedes ver el código completo que debes incluir en el archivo App.vue, reemplazando al código existente:

<template>
  <div id="app" class="container">
    <div class="row">
      <div class="col-md-12">
        <h1>Personas</h1>
      </div>
    </div>
    <div class="row">
      <div class="col-md-12">
        <tabla-personas />
      </div>
    </div>
  </div>
</template>

<script>
  import TablaPersonas from '@/components/TablaPersonas.vue'

  export default {
    name: 'app',
    components: {
      TablaPersonas,
    },
  }
</script>

<style>
  button {
    background: #009435;
    border: 1px solid #009435;
  }
</style>

Si ves de nuevo el proyecto en tu navegador, deberías ver este resultado:

A continuación reemplazaremos los datos estáticos de la tabla por datos dinámicos.

Agrega datos al componente

Actualmente solamente tenemos texto estático en la tabla de nuestro componente. Antes de continuar, para aquellos que hayan usado React, debéis saber que el método data de Vue equivale al estado de los componentes en React.

Vamos a reemplazar los datos de las personas por un array de objetos que contengan los datos de las personas. Por ello, vamos a agregar el método data a nuestra aplicación en el archivo App.vue, que sencillamente devolverá los datos de los usuarios. Además, cada una tendrá un identificador:

// ...
import TablaPersonas from '@/components/TablaPersonas.vue'

export default {
  name: 'app',
  components: {
    TablaPersonas,
  },
  data() {
    return {
      personas: [
        {
          id: 1,
          nombre: 'Jon',
          apellido: 'Nieve',
          email: 'jon@email.com',
        },
        {
          id: 2,
          nombre: 'Tyrion',
          apellido: 'Lannister',
          email: 'tyrion@email.com',
        },
        {
          id: 3,
          nombre: 'Daenerys',
          apellido: 'Targaryen',
          email: 'daenerys@email.com',
        },
      ],
    }
  },
}
// ...

Una vez hemos agregado los datos en el componente App.js, debemos pasárselos al componente TablaPersonas. Los datos se transfieren como propiedades, y se incluyen con la sintaxis :nombre="datos". Es decir, que se tratan igual que un atributo HTML, salvo por el hecho de tener dos puntos : delante:

<tabla-personas :personas="personas" />

Alternativamente también puedes usar v-bind: en lugar de :, que es la forma larga de agregar propiedades:

<tabla-personas v-bind:personas="personas" />

A continuación debemos aceptar los datos de las personas en el componente TablaPersonas. Para ello debemos definir la propiedad personas en el objeto props. El objeto props debe contener todas aquellas propiedades que va a recibir el componente, conteniendo pares que contienen el nombre de la propiedad y el tipo de las misma. En nuestro caso, la propiedad personas es un Array:

// ...
export default {
  name: 'tabla-personas',
  props: {
    personas: Array,
  },
}
...

También es posible agregar las propiedades como un array de cadenas, aunque es menos habitual:

...
export default {
  name: 'tabla-personas',
  props: ['empleados'],
}
// ...

Agrega un bucle al componente

Una vez hemos agregado los datos al componente TablaPersonas, vamos a crear un bucle que recorra los datos de las personas, mostrando una fila de la tabla en cada iteración. Para ello usaremos el atributo v-for, que nos permitirá recorrer los datos de una propiedad, que en nuestro caso es la propiedad personas:

<template>
  <div id="tabla-personas">
    <table class="table">
      <thead>
        <tr>
          <th>Nombre</th>
          <th>Apellido</th>
          <th>Email</th>
        </tr>
      </thead>
      <tbody>
        <tr v-for="persona in personas" :key="persona.id">
          <td>{{ persona.nombre}}</td>
          <td>{{ persona.apellido }}</td>
          <td>{{ persona.email}}</td>
        </tr>
      </tbody>
    </table>
  </div>
</template>

Si ahora ves las aplicación en tu navegador, verás que en apariencia no ha cambiado, aunque ahora es mucho más dinámica, pudiendo incluso agregar dos tablas usando el mismo componente:

En el siguiente apartado veremos cómo agregar nuevas filas a la tabla.

Creación de formularios

A continuación vamos a ver cómo puedes agregar nuevas personas al componente TablaPersonas. Para ello crearemos otro componente que incluya un pequeño formulario.

Crea un formulario con Vue

Vamos a crear el archivo FormularioPersona.vue en la carpeta src/componentes, en el que agregaremos un formulario con un campo input para el nombre, otro para el apellido y otro para el email de la persona a agregar. Finalmente también agregaremos un botón de tipo submit que nos permita enviar los datos.

En cuanto al código JavaScript, crearemos la propiedad persona como una propiedad que será devuelta por el componente, que incluirá el nombre, el apellido y el email de la persona que se agregue. Este sería el código del componente:

<template>
  <div id="formulario-persona">
    <form>
      <div class="container">
        <div class="row">
          <div class="col-md-4">
            <div class="form-group">
              <label>Nombre</label>
              <input type="text" class="form-control" />
            </div>
          </div>
          <div class="col-md-4">
            <div class="form-group">
              <label>Apellido</label>
              <input type="text" class="form-control" />
            </div>
          </div>
          <div class="col-md-4">
            <div class="form-group">
              <label>Email</label>
              <input type="email" class="form-control" />
            </div>
          </div>
        </div>
        <div class="row">
          <div class="col-md-4">
            <div class="form-group">
              <button class="btn btn-primary">Añadir persona</button>
            </div>
          </div>
        </div>
      </div>
    </form>
  </div>
</template>

<script>
  export default {
    name: 'formulario-persona',
    data() {
      return {
        persona: {
          nombre: '',
          persona: '',
          apellido: '',
        },
      }
    },
  }
</script>

<style scoped>
  form {
    margin-bottom: 2rem;
  }
</style>

También hemos agregado un margen al formulario con CSS en la sección style.

Agrega el formulario a la aplicación

A continuación vamos a editar el archivo App.js y a agregar el componente FormularioPersona que hemos creado:

<template>
  <div id="app" class="container">
    <div class="row">
      <div class="col-md-12">
        <h1>Personas</h1>
      </div>
    </div>
    <div class="row">
      <div class="col-md-12">
        <formulario-persona />
        <tabla-personas :personas="personas" />
      </div>
    </div>
  </div>
</template>
<script>
  import TablaPersonas from '@/components/TablaPersonas.vue'
  import FormularioPersona from '@/components/FormularioPersona.vue'

  export default {
    name: 'app',
    components: {
      TablaPersonas,
      FormularioPersona,
    },
    data: {
      // ...
    },
  }
</script>

Si ahora accedes al proyecto en tu navegador, verás que se muestra el formulario encima de la tabla:

Enlaza los campos del formulario con su estado

A continuación debemos obtener los valores que se introduzcan en el formulario mediante JavaScript, de modo que podamos asignar sus valores al estado del componente.

Para ello usamos el atributo v-model, que enlazará el valor de los campos con sus respectivas variables de estado, que son las definidas en la propiedad persona de la sentencia return del componente:

<template>
  <div id="formulario-persona">
    <form>
      <div class="container">
        <div class="row">
          <div class="col-md-4">
            <div class="form-group">
              <label>Nombre</label>
              <input v-model="persona.nombre" type="text" class="form-control" />
            </div>
          </div>
          <div class="col-md-4">
            <div class="form-group">
              <label>Apellido</label>
              <input v-model="persona.apellido" type="text" class="form-control" />
            </div>
          </div>
          <div class="col-md-4">
            <div class="form-group">
              <label>Email</label>
              <input v-model="persona.email" type="email" class="form-control" />
            </div>
          </div>
        </div>
        <div class="row">
          <div class="col-md-4">
            <div class="form-group">
              <button class="btn btn-primary">Añadir persona</button>
            </div>
          </div>
        </div>
      </div>
    </form>
  </div>
</template>

Si consultas el resultado en tu navegador y accedes a las DevTools de Vue, podrás ver cómo cambia el estado del componente cada vez que modificas un campo del formulario.

Sin embargo, todavía tenemos que enviar los datos a nuestro componente principal, que es la aplicación App en sí misma, de modo que también se modifique su estado, agregando los datos de una nueva persona a la lista de personas.

Agrega un método de envío al formulario

Vamos a agrega un evento, también conocido como event listener, al formulario. En concreto, agregaremos un evento onSubmit para que se ejecute un método cuando se haga clic en el botón de envío. Para ello usaremos al atributo @submit, que es la forma corta del atributo v-on:submit, siendo ambos equivalentes.

Información! En general todos los listeners de eventos en Vue se agregan con los prefijos @ o v-on:. Por ello, podríamos detectar un clic con @clic o v-on:clic, y un evento hover con @mouseover o v-on:mouseover.
Además, también deberíamos ejecutar el método event.preventDefault, para evitar que se refresque la página al enviar el formulario, tal y como puedes consultar aquí. Para ello, el evento @submit cuenta con el modificador prevent, que es equivalente a ejecutar el método event.preventDefault() en el interior de la función asociada al evento submit.

Vamos a asociar el método enviarFormulario al evento @submit del formulario:

<form @submit.prevent="enviarFormulario">
...
</form>

Ahora vamos a agregar el método enviarFormulario al componente. Los métodos de los componentes de Vue se incluyen en el interior de la propiedad methods, que también vamos a crear:

export default {
  name: 'formulario-persona',
  data() {
    return {
      persona: {
        nombre: '',
        persona: '',
        apellido: '',
      },
    }
  },
  methods: {
    enviarFormulario() {
      console.log('Funciona!');
    },
  },
}

Si pruebas el código y envías el formulario, verás que por la consola se muestra el texto Funciona!.

Emite eventos del formulario a la aplicación

Ahora necesitamos enviar los datos de la persona que hemos agregado a nuestra aplicación App, para ello usaremos el método $emit en el método enviarFormulario. El método $emit envía el nombre del evento que definamos y los datos que deseemos al componente en el que se ha renderizado el componente actual.

En nuestro caso, enviaremos la propiedad persona y un evento al que llamaremos add-persona:

// ...
enviarFormulario() {
  this.$emit('add-persona', this.persona);
}
// ...

Es importante que tengas en cuenta que el nombre de los eventos debe seguir siempre la sintaxis kebab-case. El evento emitido permitirá iniciar el método que reciba los datos en la aplicación App.

Recibe eventos de la tabla en la aplicación

El componente FormularioPersona envía los datos a través del evento add-persona. Ahora debemos capturar los datos en la aplicación. Para ello, agregaremos la propiedad @add-persona en la etiqueta formulario-persona mediante la cual incluimos el componente. En ella, asociaremos un nuevo método al evento, al que llamaremos agregarPersona:

<formulario-persona @add-persona="agregarPersona" />

Seguidamente, creamos el método agregarPersona en la propiedad methods del archivo App.vue, que modificará el array de personas que se incluye, agregando un nuevo objeto al mismo:

methods: {
  agregarPersona(persona) {
    this.personas = [...this.personas, persona];
  }
}

Hemos usado el operador spread de propagación ..., útil para combinar objetos y arrays, para crear un nuevo array que contenga los elemento antiguos del array personas junto con la nueva.

Si ahora ejecutas las DevTools y envías el formulario, verás que se agrega un nuevo elemento al array de personas:

Sin embargo, es importante que asignemos un ID único al elemento que acabamos de crear. Habitualmente, insertaríamos la persona creada en una base de datos, que nos devolvería la persona junto con un nuevo ID. Sin embargo, por ahora nos limitaremos a generar un ID basándonos en el ID del elemento inmediatamente anterior al actual:

methods: {
  agregarPersona(persona) {
    let id = 0;
    
    if (this.personas.length > 0) {
      id = this.personas[this.personas.length - 1].id + 1;
    }
    
    this.personas= [...this.personas, { ...persona, id}];
  }
}

Los que hemos hecho es aumentar el valor del ID del último elemento agregado en una unidad, o dejarlo en 0 si no hay elementos. Luego insertamos la persona en el array, a la que agregamos el id generado.

Validaciones von Vue

Nuestro formulario funciona, y hasta es necesario introducir una dirección de email válida gracias a la validación HTML de muchos navegadores. Sin embargo, todavía tenemos que mostrar una notificación cuando un usuario se inserte correctamente, reestablecer el foco en el primer elemento del formulario y vaciar los campos de datos. Además, debemos asegurarnos de que se han rellenado todos los campos con datos válidos, mostrando un mensaje de error en caso contrario.

Propiedades computadas de Vue

En Vue, los datos se suelen validar mediante propiedades computadas o computed properties, que son funciones que se ejecutan automáticamente cuando se modifica el estado de alguna propiedad. De este modo evitamos sobrecargar el código HTML del componente. Las propiedades computadas se agregan en el interior de la propiedad computed, que añadiremos justo después de la propiedad methods del componente FormularioPersona:

// ...
computed: {
  nombreInvalido() {
    return this.persona.nombre.length < 1;
  },
  apellidoInvalido() {
    return this.persona.apellido.length < 1;
  },
  emailInvalido() {
    return this.persona.email.length < 1;
  },
},
// ...

Hemos agregado una validación muy sencilla que simplemente comprueba que se haya introducido algo en los campos.

A continuación vamos a incluir una variable de estado en el componente FormularioPersona a la que llamaremos procesando, que comprobará si el formulario se está enviando actualmente o no.

También agregaremos las variables error y correcto.  El valor de la variable error será true si el formulario se ha enviado correctamente o false si ha habido algún error. Del mismo modo, el valor de la variable correcto será true si el formulario se ha enviado correctamente o false en caso contrario:

// ...
data() {
  return {
    procesando: false,
    correcto: false,
    error: false,
    persona: {
      nombre: '',
      apellido: '',
      email: '',
    }
  }
}
// ...

Seguidamente, debemos modificar el método enviarFormulario para que establezca el valor de la variable de estado procesando como true cuando se esté enviado el formulario, y como false cuando se obtenga un resultado. En función del resultado obtenido, se establecerá el valor de la variable error como true si ha habido algún error, o el valor de la variable correcto como true si se han enviado los datos correctamente:

// ...
methods: {
  enviarFormulario() {
    this.procesando = true;
    this.resetEstado();
    
    // Comprobamos la presencia de errores
    if (this.nombreInvalido || this.apellidoInvalido || this.emailInvalido) {
      this.error = true;
      return;
    }
    
    this.$emit('add-persona', this.persona);

    this.error = false;
    this.correcto = true;
    this.procesando = false;

    // Restablecemos el valor de la variables
    this.persona= {
      nombre: '',
      apellido: '',
      email: '',
    }
  },
  resetEstado() {
    this.correcto = false;
    this.error = false;
  }
}
// ...

Como ves, hemos agregado también el método resetEstado para resetear algunas variables de estado.

Sentencias condicionales con Vue

A continuación vamos a modificar el código HTML de nuestro formulario. Tal y como has podido comprobar ya al observar las DevTools, Vue renderiza de nuevo cada componente cada vez que se modifica el estado de un componente. De este modo, los eventos se gestionan con mayor facilidad.

Dicho esto, vamos a configurar el formulario de modo que se agregue la clase CSS is-invalid a los campos del mismo en función de sin han fallado o no. También agregaremos el posible mensaje de error al final del formulario

<form @submit.prevent="enviarFormulario">
  <div class="container">
    <div class="row">
      <div class="col-md-4">
        <div class="form-group">
          <label>Nombre</label>
          <input
            v-model="persona.nombre"
            type="text"
            class="form-control"
            :class="{ 'is-invalid': procesando && nombreInvalido }"
            @focus="resetEstado"
          />
        </div>
      </div>
      <div class="col-md-4">
        <div class="form-group">
          <label>Apellido</label>
          <input
            v-model="persona.apellido"
            type="text"
            class="form-control"
            :class="{ 'is-invalid': procesando && apellidoInvalido }"
            @focus="resetEstado"
        />
        </div>
      </div>
      <div class="col-md-4">
        <div class="form-group">
          <label>Email</label>
          <input
            v-model="persona.email"
            type="email"
            class="form-control"
            :class="{ 'is-invalid': procesando && emailInvalido }"
            @focus="resetEstado" />
        </div>
      </div>
    </div>
    <div class="row">
      <div class="col-md-4">
        <div class="form-group">
         <button class="btn btn-primary">Añadir persona</button>
        </div>
      </div>
    </div>
  </div>
  <div class="container">
    <div class="row">
      <div class="col-md-12">
        <div v-if="error && procesando" class="alert alert-danger" role="alert">
          Debes rellenar todos los campos!
        </div>
        <div v-if="correcto" class="alert alert-success" role="alert">
          La persona ha sido agregada correctamente!
        </div>
      </div>
    </div>
  </div>
</form>

Tal y como has visto, hemos usado el tributo :class para definir las clases, ya que no podemos usar atributos que existan actualmente en HTML. El motivo de no usar el atributo class es que acepta únicamente una cadena de texto como valor.

También hemos agregado el evento @focus a los campos input para que se reseteen los estados cada vez que se seleccionen.

Para mostrar los mensajes de error hemos usado la sentencia condicional v-if de Vue, que hará que el elemento en el que se incluya solamente se muestre si la condición especificada se evalúa como true.

El mensaje de error solamente se mostrará si el valor de la variable error es true y el formulario se está procesando. El mensaje de éxito se mostrará únicamente cuando el valor de la variable correcto sea true.

También es posible usar sentencias v-else o v-else-if, que funcionan exactamente igual que las sentencias else y else if de JavaScript respectivamente.

Para más información acerca del renderizado condicional, consulta la documentación de Vue.

Tras agregar los mensajes de error, echa de nuevo un ojo al navegador para ver el resultado. Verás que cuando te olvidas de rellnear algún campo se muestra un mensaje de error:

Del mismo modo, se mostrará el mensaje de éxito cuando la persona se agregue correctamente a la lista:

Referencias con Vue

Cuando envías un formulario, lo normal es que tanto el cursor como el foco, por temas de accesibilidad web, se sitúen en el primer elemento del formulario, que en nuestro caso es el campo nombre. Para ello podemos usar las referencias de Vue, ya que permiten hacer referencia a los elementos que las incluyen. Para agregar una referencia debes usar el atributo ref.

A continuación vamos a agregar una referencia al campo nombre:

<input
  ref="nombre"
  v-model="persona.nombre"
  type="text"
  class="form-control"
  :class="{ 'is-invalid': procesando && nombreInvalido }"
  @focus="resetEstado"
  @keypress="resetEstado"
/>

Finalmente, tras enviar el formulario, vamos a usar el método focus que incluyen las referencias para que el cursor se sitúe en el campo nombre:

this.$emit('add-persona', this.persona);
this.$refs.nombre.focus();

También hemos agregado un evento @keypress al campo nombre para que el estado se resetee cuando se pulse una tecla, puesto que el foco ya estará en dicho campo.

Si ahora pruebas a agregar una persona, verás que el cursor se sitúa en el primer elemento.

Elimina elementos con Vue

El formulario ya funciona correctamente, pero vamos a agregar también la opción de poder borrar personas.

Agrega un botón de borrado a la tabla

Para ello, vamos a agregar una columna más a la tabla del componente TablaPersonas, que contendrá un botón que permita borrar cada fila:

<template>
  <div id="tabla-personas">
    <table class="table">
      <thead>
        <tr>
          <th>Nombre</th>
          <th>Apellido</th>
          <th>Email</th>
          <th>Acciones</th>
        </tr>
      </thead>
      <tbody>
        <tr v-for="persona in personas" :key="persona.id">
          <td>{{ persona.nombre}}</td>
          <td>{{ persona.apellido }}</td>
          <td>{{ persona.email}}</td>
          <td>
              <button class="btn btn-danger">🗑️ Eliminar</button>
          </td>
        </tr>
      </tbody>
    </table>
  </div>
</template>

Emite un evento de borrado desde la tabla

Ahora, al igual que hemos hecho en el formulario, debemos emitir un evento al que llamaremos eliminarPersona. Este evento enviará el id de la persona a eliminar al componente padre, que en este caso es la aplicación App.vue:

<button class="btn btn-danger" @click="$emit('delete-persona', persona.id)">🗑️ Eliminar</button>

Recibe el evento de borrado en la aplicación

Ahora, en la aplicación App.vue debes agregar una acción que ejecute un método que elimina a la persona correspondiente con el id recibido:

<tabla-personas :personas="personas" @delete-persona="eliminarPersona" />

Ahora define el método eliminarPersona justo debajo del método agregarPersona que hemos creado anteriormente:

eliminarPersona(id) {
  this.personas = this.personas.filter(
    persona => persona.id !== id
  );
}

Hemos usado el método filter, que conservará aquellos elementos del array personas cuyo id no sea el indicado. Para más información acerca de cómo usar este método, consulta el siguiente tutorial, en donde explico cómo filtrar un array en JavaScript.

Y con esto, si consultas el navegador, podrás comprobar que las personas se eliminan de la tabla al pulsar el botón de su respectiva fila:

Agrega un mensaje informativo

Para que la aplicación sea más usable, vamos a agregar un mensaje que se mostrará justo antes de la etiqueta table de apertura cuando ésta no contenga personas:

<div v-if="!personas.length" class="alert alert-info" role="alert">
  No se han agregado personas
</div>

Este es el mensaje que se mostrará cuando no queden usuarios:

Edita elementos con Vue

Ahora que podemos eliminar elementos, tampoco estaría mal poder editarlos, que es lo que haremos en este apartado. Vamos a agregar la posibilidad de editar los elementos en la propia tabla, por lo que no necesitaremos componentes adicionales.

Agrega un botón de edición a la tabla

Comenzaremos agregando un botón edición a la tabla del componente TablaPersonas, justo al lado del botón de borrado:

<button class="btn btn-info ml-2" @click="editarPersona(persona)">✏️ Editar</button>

El botón ejecutará el método editarPersona, al que pasará la persona que seleccionada.

Agrega un método de edición a la tabla

A continuación vamos a agregar el método editarPersona a nuestra lista de métodos, en el interior de la propiedad methods de nuestro componente:

methods: {
  editarPersona(persona) {
    this.personaEditada = Object.assign({}, persona);
    this.editando = persona.id;
  },
}

Lo que hemos hecho ha sido almacenar los datos originales de la persona que se está editando actualmente en el objeto personaEditada, de forma que podemos recuperar los datos si se cancela la edición.

Además, hemos cambiado el valor de la variable de estado editando, que como no podría ser de otro modo, debemos agregar a nuestro componente:

data() {
  return {
    editando: null,
  }
},

Agrega campos de edición a la tabla

Lo que vamos a hacer es comprobar el valor de la variable editando en cada fila, mostrando campos input en lugar de los valores de cada persona en la fila que se esté editando:

<template>
  <div id="tabla-personas">
    <div v-if="!personas.length" class="alert alert-info" role="alert">
      No se han agregado personas
    </div>
    <table class="table">
      <thead>
        <tr>
          <th>Nombre</th>
          <th>Apellido</th>
          <th>Email</th>
          <th>Acciones</th>
        </tr>
      </thead>
      <tbody>
        <tr v-for="persona in personas" :key="persona.id">
          <td v-if="editando === persona.id">
              <input type="text" class="form-control" v-model="persona.nombre" />
          </td>
          <td v-else>
              {{ persona.nombre}}
          </td>
          <td v-if="editando === persona.id">
              <input type="text" class="form-control" v-model="persona.apellido" />
          </td>
          <td v-else>
              {{ persona.apellido}}
          </td>
          <td v-if="editando === persona.id">
              <input type="email" class="form-control" v-model="persona.email" />
          </td>
          <td v-else>
              {{ persona.email}}
          </td>
          <td>
                <button class="btn btn-info" @click="editarPersona(persona)">✏️ Editar</button>
                <button class="btn btn-danger ml-2" @click="$emit('delete-persona', persona.id)">🗑️ Eliminar</button>
          </td>
        </tr>
      </tbody>
    </table>
  </div>
</template>

Ahora, si pruebas la aplicación y editas una fila, podrás ver que se muestran campos de edición en las filas de la persona que estás editando:

Agrega un botón de guardado a la tabla

Sin embargo, todavía necesitamos un botón de guardado y otro que permita cancelar el estado de edición. Estos botones se mostrarán únicamente en la fila que se está editando:

<td v-if="editando === persona.id">
    <button class="btn btn-success" @click="guardarPersona(persona)">💾 Guardar</button>
    <button class="btn btn-secondary ml-2" @click="cancelarEdicion(persona)">❌ Cancelar</button>
</td>
<td v-else>
  <button class="btn btn-info" @click="editarPersona(persona)">✏️ Editar</button>
  <button class="btn btn-danger ml-2" @click="$emit('delete-persona', persona.id)">🗑️ Eliminar</button>
</td>

Como ves, hacemos referencia al método guardarPersona, que todavía tenemos que agregar. Por ahora, este sería el resultado cuando haces clic en el botón de edición de alguna fila:

Emite el evento de guardado

Ahora, vamos a agregar el método guardarPersona a la lista de métodos. Este método enviará un evento de guardado a la aplicación App.js, que se encargará de actualizar los datos de la persona que hemos editado:

guardarPersona(persona) {
  if (!persona.nombre.length || !persona.apellido.length || !persona.email.length) {
    return;  
  }
  this.$emit('actualizar-persona', persona.id, persona);
  this.editando = null;
}

Agrega un método que cancele la edición

Vamos a agregar también el método cancelarEdicion que también hemos referenciado en el apartado anterior, que permite cancelar el estado de edición de una persona:

cancelarEdicion(persona) {
  Object.assign(persona, this.personaEditada);
  this.editando = null;
}

Ta, y como ves, cuando cancelamos la edición de una persona, recuperamos su valor original, almacenado en el objeto personaEditada.

Recibe el evento de actualización en la aplicación

Todavía tenemos que modificar el código de la aplicación App.js para que reciba el evento de guardado y actualice los datos de la persona indicada. Primero agregaremos el evento actualizar-persona:

<tabla-personas
  :personas="personas"
  @delete-persona="eliminarPersona"
  @actualizar-persona="actualizarPersona"
/>

Ahora vamos a agregar el método actualizarPersona a la lista de métodos de la aplicación, justo después del método eliminarPersona:

actualizarPersona(id, personaActualizada) {
  this.personas = this.personas.map(persona =>
    persona.id === id ? personaActualizada : persona
  )
}

En el método anterior hemos usado la estructura map para recorrer el array de personas, actualizando aquella que coincida con el id de la persona que queremos actualizar.

Si ahora pruebas la aplicación, verás que los cambios se guardan correctamente

Build & Deploy de la aplicación Vue

Hemos creado una aplicación que funciona en nuestro navegador, pero lo ideal sería que funcionase en un servidor externo, de forma que podamos acceder a ella desde cualquier parte. Para ello vamos a usar GitHub Pages, que permite hospedar webs estáticas. De esta forma que no te tendrás que preocupar de configurar un servidor.

Podría dar por hecho que has creado un repositorio para este proyecto y que todo tu código ya está en la rama main de GitHub.

En caso de que nunca hayas usado Git, primero instala Git en tu sistema y luego inicializa un repositorio en el directorio raíz del proyecto ejecutando git init. Luego accede a tu cuenta de GitHub y crea un nuevo repositorio, que tendrá la estructura https://github.com/usuario/repositorio, siendo usuario tu nombre de usuario de GitHub y repositorio el nombre del repositorio.

Seguidamente ejecuta estos comandos desde el directorio raíz del proyecto para subir tu código a GitHub, reemplazando usuario por tu nombre de usuario:

git add .
git remote add origin https://github.com/usuario/tutorial-vue
git commit -m "Primer commit"
git push -u origin main

Con esto ya debería estar nuestro código en GitHub.

Información! Si nunca has usado Git, te recomiendo que consultes el tutorial de introducción a Git.

Build de la aplicación Vue

La aplicación que estás usando se compila al vuelo en memoria. Al tratarse de una versión en desarrollo, se realizan comprobaciones de errores y diversos tests que provocan que no sea una versión apta para usar en producción. Para compilar los archivos y crear una versión para producción debes cerrar el proceso actual de node pulsando CTRL+C o CMD+C y ejecutar uno de los siguientes comandos, dependiendo del gestor de paquetes que utilices:

# NPM
npm run build

#Yarn
yarn build

Deploy de la aplicación Vue

Vamos a desplegar la aplicación en GitHub Pages. Primero tendremos que hacer algunos cambios, así que vamos a crear una nueva rama llamada gh-pages:

git checkout -b gh-pages

Ahora edita el archivo .gitignore y elimina el directorio /dist del mismo, ya que debemos enviarlo a GitHub.

Luego crea el archivo vue.config.jsen el directorio raíz del proyecto y agrega el directorio en el que residirá la aplicación. En mi caso, el proyecto va a estar ubidado en la URL https://neoguias.github.io/tutorial-vue/, por lo que tendré que agregar el directorio /tutorial-vue, pero puedes usar cualquier otro:

module.exports = {
  publicPath: '/tutorial-vue',
}

Seguidamente debes hacer un build de la aplicación:

# NPM
npm run build

#Yarn
yarn build

Ahora, una vez creada la verisón de producción de la aplicación en el directorio /dist, debes agregarla al repositorio:

git add dist

Ahora haz un commit con los cambios realizados:

git commit -m "gh-pages commit"

Y finalmente haz un push a GitHub con los cambios, agregando el prefijo dist:

git subtree push --prefix dist origin gh-pages

Ahora, si accedes a la URL https://usuario.github.io/tutorial-vue/, reemplazando usuario por tu nombre de usuario y tutorial-vue por el nombre del proyecto, deberías poder ver la aplicación funcionando online.

A continuación puedes ver los enlaces del proyecto en GitHub:

Y esto ha sido todo.

Cómo continuar tu aprendizaje

En este tutorial has aprendido bastantes cosas de Vue, ya que hemos creado una aplicación CRUD con Vue. Has aprendido a crear componentes, métodos y formularios. Ahora también sabes usar estados y crear eventos, expresiones condicionales y más cosas. Finalmente, también has aprendido a poner una aplicación en producción.

Por ahora, esta aplicación solamente funciona en tu navegador. Si refrescas el navegador, los datos se perderán. Lo que podríamos hacer ahora es conectar la aplicación con un servidor externo, de modo que los datos se guarden permanentemente. Pero este tutorial ha resultado ser bastante extenso, así que lo dejaremos para el siguiente tutorial de Vue, que estará disponible muy pronto.

Lo que haremos en el siguiente tutorial, será crear una aplicación que soporte la vista (GET), creación (POST), actualización (PUT) y eliminación (DELETE) de los datos de diversas entidades. Es decir, que crearemos una aplicación que siga la estructura REST. Haremos esto valiéndonos de una API, por lo que agregaremos las acciones necesarias para conectar nuestra aplicación con la API.

Me ha llevado bastantes horas crear este tutorial, así que, si crees que te ha sido útil, agradecería que lo compartieras con más personas a las que podría interesarles.


Avatar de Edu Lazaro

Edu Lázaro: Ingeniero técnico en informática, actualmente trabajo como desarrollador web y programador de videojuegos.

👋 Hola! Soy Edu, me encanta crear cosas y he redactado esta guía. Si te ha resultado útil, el mayor favor que me podrías hacer es el de compatirla en Twitter 😊

Si quieres conocer mis proyectos, sígueme en Twitter.

25 comentarios en “Tutorial de introducción a Vue 3

  1. Hola Edu, el tuto me iba todo muy bien hasta que en el punto 10.2 hay este código
    methods: {
    editarPersona(persona) {
    this.personaEditada = Object.assign({}, persona);
    this.editando = id;
    },
    }
    en la linea has puesto que el this.editando es igual a «id» pero en verdad es «persona.id»
    esas 7 letras me han jodido por dos noches tioo!!

    saludos! :)

      1. Hola, creo que sigue el error. Porque el evento click envía el id como parametro

        @click=»editarPersona(persona.id)»

        Pero en el metodo se recibe como objeto persona.

        editarPersona(persona) {
        this.personaEditada = Object.assign({}, persona);
        this.editando = persona.id;
        },

        Una solucipon es que his.editando debe ser entonces = persona porque el id ya no tiene otra propiedad id, la variable enviada ya tiene le valor del id.

        La otra opcióne corregir el envio del argumento, enviando así solo persona y no persona.id

        Creo que la última es la mejor.

        Gracias por tan detalladas instrucciones. Muy buen post.

  2. muchas gracias por esta guía, me ha servido bastante, para iniciarme al mundo del frontend, y este tutorial, está muy bien explicado y estructurado, desde lo más básico, como se compone un componente hasta lo más avanzado, que es desplegarlo en producción. Mil gracias por esta obra maestra.

  3. Muchas gracias. Eres un genio. Me servirá patra aprender Vue. La verdad es que domino React y Angular y tenía interés por conocer este framework. Me has salvado la vida

  4. Muchisimas gracias por dedicar tu tiempo a crear este tutorial.
    Lo he completado entero y ha sido un delicia hacerlo.
    Las cosas han funcionado a la primera y todas las explicaciones has sido muy claras y entendibles para personas que estan empezando con Vue

  5. Gracias profe….Estoy aprendiendo este framework y este tutorial me da a conocer muchos conceptos teoricos que se aplican en la codificación. Voy a tratar de contactarme con Usted. Saludos desde Argentina/Misiones exactamente.

  6. Me ha parecido excelente tu tutorial, muy claro, bien explicado.
    Estoy dando los primero pasos con «vue» pero con este tutorial
    voy Super !!!
    Excelente trabajo, Muchas Gracias por el valioso aporte.
    Saludos.

Responder a Mario Cancelar la respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

“- Hey, Doc. No tenemos suficiente carretera para ir a 140/h km. - ¿Carretera? A donde vamos, no necesitaremos carreteras.”