Original article: How to Migrate from Vue v.2 to Vue v.3 with a Simple Example Project

¿Qué es Vue.js?

Vue.js es un marco de trabajo para interfaces gráficas web (del inglés: "front-end framework") creado por Evan You. Es uno de los más poderosos y fáciles de usar, con más de 9.5 millones de descargas mensuales.

En Septiembre 2020 se publicó la version nueva version Vue 3 core. Este nuevo Vue introduce increíbles nuevas características pero también algunos cambios disruptivos (que lo hace incompatible con versiones anteriores).

¿Por qué debería migrar a Vue3?

Así cómo la industria tecnológica está en constante evolución, también así las librerías, lenguajes y marcos de trabajo (frameworks). En cada nueva versión se arreglan errores y se introducen nuevas características. Y frecuentemente con cada versión mayor, se mejora el flujo de trabajo para los desarrolladores. Las nuevas funcionalidades pueden darte la oportunidad lograr con facilidad cosas que anteriormente eran consideradas tareas tediosas.

Vue 3 aún es relativamente nuevo y no es urgente migrar todos tus proyectos, pero eventualmente llegará el momento en que Vue 2 ya no recibirá soporte. Por tal motivo, es buena idea conocer qué pasos necesitarás para poder realizar la migración de tus proyectos.

En esta guía te llevaré de la mano a través de los pasos básicos que necesitas realizar para familiarizarte y llevar a cabo una migración a Vue 3. Para lo cual crearemos un proyecto y luego lo migraremos a Vue 3.

El proyecto que vamos a elaborar es intencionalmente uno muy simple, esto para que cualquiera pueda seguir los pasos y aprender. Mientras más complejo sea tu proyecto, más cuidado querrás tener para planear y llevar a cabo la migración.

Introducción

La nueva versión de Vue.js sí contiene algunos cambios disruptivos, además de nuevas capacidades y características. También, hay librerías populares como Vue Router que han sido actualizadas para incluir soporte para la nueva versión de Vue.

Si ya sabes usar Vue 2, las bases son prácticamente las mismas. Pero antes que puedas migrar un proyecto a Vue 3 hay cambios que necesitas tomar en consideración.

Dependiendo del tamaño del proyecto que quieras migrar, asegúrate considerar todos los cambios que han sido introducidos en la nueva versión para que tu aplicación pueda seguir funcionando correctamente luego de la migración.

Para este tutorial, vamos a mantener las cosas sencillas y te mostraré cómo migrar un proyecto Vue.js que actualmente usa Vue 2 a través de un enlace CDN.

Estoy tomand0 este proyecto de un libro que escribí (sólo disponible en inglés) para freeCodeCamp; puedes encontrarlo aquí.

En dicho proyecto usamos Vue Router, por lo que en este artículo también daremos un vistazo a lo nuevo en Vue Router.

Lo que necesitas para seguir esta guía

Para seguir esta guía necesitas conocimiento básico de Vue.js y Vue Router. Sino tienes conocimientos básicos de Vue y Vue Router te sugiero que empieces aprendiendo acerca de estos temas primero.

Lo que cubriremos en este artículo

Este tutorial está organizado en 3 capítulos. Primero daremos un vistazo a los cambios en Vue.js v3.x para luego dar una breve revisión de Vue Router v4.x. Y finalmente planearemos la migración del proyecto real.

  • un vistazo a Vue v3.x
  • cambios disruptivos para Vue v2.x
  • un vistazo a Vue Router v4.x
  • cambios disruptivos para Router v3.x
  • migración del proyecto portafolio
  • clonar el repositorio
  • actualizar los CDN scripts
  • actualizar la instancia Vue
  • actualizar la instancia Vue Router

Un vistazo a Vue v3.x

Vue 3 introduce algunas nuevas características y un montón de cambios disruptivos. Veamos cómo estos cambios afectarán a tu aplicación y considerémoslos antes de realizar la migración.

Vue V3.x Cambios disruptivos

En Vue 3 los cambios disruptivos básicamente caen en 7 categorías:

  • API Global (responsable del comportamiento de Vue) - es muy probable que quieras dar un vistazo a estos cambios.
  • Directivas de templete (cambios en la forma en que funcionan las directivas "v-{directiva}") - es muy probable que quieras dar un vistazo a estos cambios.
  • Componentes (cambios en su funcionamiento) - es muy probable que quieras dar un vistazo a estos cambios.
  • La función de renderizado o bien "render function" (para la creación programática de elementos HTML).
  • Elementos "custom" (elementos personalizados - informa a Vue de la creación de elementos HTML personalizados)
  • Cambios menores (estos podrías no afectarte, pero de todas formas querrás revisarlos)
  • APIs que han sido removidas (que ya no estarán disponibles para su uso en Vue 3)

Entre todos los cambios hay algunos de ellos que son utilizados por cualquier aplicación que utilice Vue, cómo el API Global y los componentes. Así que necesitaras tomarlos en cuenta si quieres empezar a usar la nueva versión de Vue.

También vale la pena mencionar los siguientes cambios:

  • La manera de crear aplicaciones e instancias de los componentes ha cambiado (API Global).
  • Siempre deberás declarar la opción "data" como una función (cambio menor).
  • Cambia el orden de precedencia al utilizar las directivas v-if y v-for en el mismo elemento.
  • Debes declarar la opción "emits" para los eventos de componentes.

Para la lista completa de los cambios puedes leer la documentación oficial (en inglés) o el PR (Pull Request) de la  documentación en español (en revisión para su publicación).

Demos un vistazo con mayor detalle a algunos de estos cambios.

Cómo crear una aplicación e instancias de component en Vue 3

En Vue 3 ha cambiado la manera en que se crean los componentes. El "Vue app" ahora utiliza el nuevo método .createApp() para generar nuevas instancias de aplicación.

La aplicación Vue ahora se considera cómo el componente raíz, así que la forma de definir sus opciones de datos también ha cambiado.

El elemento HTML raíz no ha cambiado por lo que todavía encontrarás en el documento index.html algo como esto:

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

Dentro del documento JavaScript hay un cambio importante que necesitarás tomar en cuenta: ya no es necesario usar new Vue() para crear una instancia nueva de "app" pero en su lugar usarás el nuevo método llamado .createApp(), veamos una comparación de sintaxis:


//Sintaxis Vue 3 

const app = Vue.createApp({
    // objecto "options" (opciones) 
})
app.mounth('#app') // Componente Raíz - Instancia Vue

//Sintaxis Vue 2 
const app = new Vue({
    // objecto "options" (opciones) 
    el: '#app'
})

Cómo definir un componente Vue 3

Para definir un componente en Vue 3 ya no se usa Vue.component(). En su lugar ahora se utiliza el componente raíz de la aplicación, así:

/* Sintaxis Vue 3 */
const app = Vue.createApp({
    // opciones aquí
})

app.component('componenet-name', {
    //código de componente aquí
})


/* Sintaxis Vue 2*/
Vue.component('component-name', {
    //código de componente aquí
})

Cómo usar el objeto "data options" en Vue 3

Dado que la instancia principal de "app" se considera un componente raíz ya no se especifica la propiedad "data" como un objeto. Ahora necesitas definirla como una función que a su vez devuelve un objeto; como usualmente se hace en los componentes.  

// Vue 3
const app = Vue.createApp({
    // objecto options 
    data(){
        return {
            message: 'hola mundo'
        }
    }
})
app.mounth('#app') // Componente raíz - Instancia Vue

// Sintaxis Vue 2 
const app = new Vue({
    // objecto options
    el: '#app'
    data: {
        message: 'hola mundo'
    }
})

Cambio de precedencia para v-if/v-for en Vue 3

En Vue 2 si usas ambas directivas en el mismo elemento lo que sucederá es que la directiva v-for tendrá precedencia sobre v-if. Pero en Vue 3, será siempre v-if la que tendrá precedencia.

Sin embargo, usar ambas directivas en el mismo elemento no es buena idea. Asegúrate de revisar la documentación al respecto.

Cómo usar la propiedad "emits" en eventos de componente en Vue 3

Este es un cambio disruptivo así como una nueva característica.

De forma similar a la propiedad props, ahora en Vue 3 también existe la propiedad emits que es utilizada por el componente para declarar eventos que "emitirá" a su componente padre.

Recomiendo muchísimo el uso de esta propiedad para evitar emitir eventos duplicados en componentes que necesitan re-emitir eventos nativos como el evento "click".

Aquí un ejemplo de la documentación oficial:

<template>
  <div>
    <p>{{ texto }}</p>
    <button v-on:click="$emit('aceptado')">OK</button>
  </div>
</template>
<script>
  export default {
    props: ['texto'],
    emits: ['aceptado']
  }
</script>

La propiedad "emits" también acepta un objeto.

Resumen Vue Router v4.x

Con el nuevo lanzamiento de Vue.js también tenemos una nueva version de Vue Router. El nuevo lanzamiento v4.x contiene algunos cambios disruptivos que debes considerar si vas a migrar un proyecto a la nueva versión de Vue.

Cambios disruptivos en Vue Router V4

Hay dos cambios disruptivos que son especialmente importantes de mencionar ya que son la base de una aplicación Vue Router, y deberás conocerlos si vas a migrar tu aplicación:

  • La instancia Vue Router cambió
  • Hay una nueva "history option"

La lista completa de cambios la puedes encontrar en la documentación oficial (sólo disponible en inglés).

Miremos a profundidad estos dos cambios.

La instancia Vue Router 4 ha cambiado

Para crear una nueva instancia Vue Router ya no se usa la función-constructor VueRouter. En cambio se hace de la siguiente forma de acuerdo con la documentación oficial (sólo en inglés).

El código ha cambiado de esto:

// Crear la instacia router y pasar la opción `routes`(rutas)
// Es posible pasar opciones adicionales, 
//  pero mantengámoslo simple por ahora
const router = new VueRouter({
  routes // abreviatura para `routes: routes`
})

// Crear y montar (mount) la instancia raíz (root).
// Asegurárse de usar la instancia router para
// que toda la aplicación tenga "conocimiento" del router.
const app = new Vue({
  router
}).$mount('#app')

A esto:

// Crear la instacia router y pasar la opción `routes`(rutas)
// Es posible pasar opciones adicionales, 
//  pero mantengámoslo simple por ahora

const router = VueRouter.createRouter({
  history: VueRouter.createWebHashHistory(),
  routes, // abreviatura para `routes: routes`
});

// Crear y montar (mount) la instancia raíz (root).
const app = Vue.createApp({})
// Asegurárse de usar la instancia router para
// que toda la aplicación tenga "conocimiento" del router

app.use(router)

app.mount('#app')

En el código anterior puedes ver que para crear una instancia Vue Router es necesario utilizar el objeto VueRouter llamando al método createRouter().

También podemos ver que la propiedad history es obligatoria: history: VueRouter.createWebHashHistory(). Si no la defines se te generará un error por la consola.

A continuación crearás la instancia Vue usando el método const app = createApp() para luego llamar al método .use() desde app.

Finalmente podrás montar el elemento raíz en la instancia app usando app.mount('#app').

Cómo migrar un proyecto portafolio de Vue 2 a Vue 3

Considerando lo visto hasta ahora y luego de una cuidadosa consideración y revisión de los cambios disruptivos, intentemos actualizar un proyecto, usaremos un proyecto de portafolio.

Necesitaremos:

  • Clonar el repo
  • Editar los scripts de las CDNs
  • Editar la instancia Vue
  • Editar la instancia Router

Para migrar tu aplicación a Vue 3 necesitaremos editar lo siguiente:

  • La instancia de aplicación Vue
  • La instancia Vue-Router
  • Enlaces a CDNs

Vayamos paso a paso.

Clonar el repositorio del proyecto

Primero asegúrate de clonar el repositorio en el folder llamado "vue-folio":

git clone https://bitbucket.org/fbhood/vue-folio/src/master/ vue-folio

Ya que nuestro proyecto utiliza CDN para cargar Vue el siguiente paso es editar los enlaces CDN.

Editar los enlaces CDN

En nuestro proyecto usamos Vue y Vue Router desde un CDN, por tal debemos actualizar ambos enlaces.

Abrir el archivo index.html y remplazar esta parte:

    <!-- VueJS 3 versión de producción  -->
    <script src="https://cdn.jsdelivr.net/npm/vue@2.6.12"></script>
    <!-- Vue Router -->
    <script src="https://unpkg.com/vue-router@2.0.0/dist/vue-router.js"></script>

con esto:

    <!-- VueJS 3  versión de producción-->
    <script src="https://unpkg.com/vue@3"></script>

    <!-- Vue Router -->
    <script src="https://unpkg.com/vue-router@4"></script>

Actualiza el código

Ahora si corres el proyecto con live server y abres el inspector notarás que la aplicación no se muestra y que hay un par de errores que se pueden ver en la consola. Ambos errores podrían relacionarse con Vue router:

You are running a development build of Vue.
Make sure to use the production build (*.prod.js) when deploying for production.

Uncaught TypeError: window.Vue.use is not a function
    at vue-router.js:1832
    at vue-router.js:9
    at vue-router.js:10

Uncaught ReferenceError: VueRouter is not defined
    at main.js:185

¡¿Vue router no está definido?!  ¿Porqué?

Bueno, recordemos que cuando Vue fue re-escrito sus librerías tuvieron que actualizar también su código. Así que, no olvidemos estos cambios disruptivos que en este caso, como podemos ver tienen relación con Vue router; ya que nuestra aplicación lo utiliza.

Actualicemos primero la instancia principal Vue para que utilice la nueva sintaxis. Luego veremos qué otros cambios necesitamos realizar para que Vue Router trabaje correctamente.

Actualiza este código que se encuentra en el archivo main.js, de esto:

// crear y montar una nueva instancia Vue

const app = new Vue({
    router
}).$mount('#app')

a esto:

// crear y montar una nueva instancia Vue

const app = Vue.createApp({
    router
})
app.mount('#app')

Cambios en Vue Router 4

Anteriormente vimos la nueva sintaxis para definir el componente raíz con base en la instancia Vue, pero ahora necesitamos tomar en cuenta los cambios disruptivos en Vue router también.

La forma de instanciar Vue Router ha cambiado

Cambió de esto:

// crear una nueva instancia VueRouter
const router = new VueRouter({
    routes
})

a esto:

// crear una nueva instancia VueRouter
const router = VueRouter.createRouter({
    // Provee una implementación de "history". Usamos "hash" por su simplicidad en este ejemplo.
    history: VueRouter.createWebHashHistory(),
    routes, // abreviación de `routes: routes`
})

El código anterior aborda dos cambios mayores: new VueRouter() ha sido remplazado con VueRouter.createRouter(), y la nueva opción history remplaza a la opción mode.

Finalmente, hagamos que nuestra aplicación sea consciente de que estamos usando Vue Router. Inyectamos la instancia del router en la instancia Vue y luego necesitaremos instruirla para que use el router utilizando el método .use() y pasar la instancia del router.

Cambiemos esto:

// crear y montar la instancia Vue

const app = Vue.createApp({
    router
})
app.mount('#app')

a esto:

// crear y montar la instancia Vue

const app = Vue.createApp({})
app.use(router)
app.mount('#app')

Ahí lo tienes!

Conclusión

No importa cuán compleja sea tu aplicación Vue, si deseas migrar a la nueva versión mayor, necesitarás un plan, leer las notas de cambios y novedades, así como revisar la lista de los cambios disruptivos para estar seguros que entiendes cuales partes podrían causar problemas.

Mientras más compleja sea la aplicación, más cuidadoso deberá ser el plan para la migración y su ejecución.

Para nuestra simple aplicación esto es todo lo que necesitamos hacer. Pero, no siempre será tan sencillo, así que prepárate anticipadamente.