Skip to content
En esta página

Vue online

En la jerga del desarrollo web la palabra producción (o production) significa que un sitio web está online, para diferenciarlo de desarrollo (o development) que es cuando estamos creando nuestra aplicación y probándola en nuestro browser con el comando npm run dev (o npm run serve si están usando Vue CLI).

Otra palabra de la jerga es deploy. Hacer un deploy significa subir nuestro sitio a un servidor público donde pueda estar online.

Hay distintas formas de hacer un deploy, algunas más simples otras más complejas, dependiendo del tipo de aplicación y del servicio de hosting.

TL;DR: La forma más simple de hacerlo es con GitHub Pages.

Deploy para una SPA

Hacer un deploy de una SPA (Single Page Application) creada con Vue no es tan complicado, ya que las SPA son Client Side Rendered. Client Side Rendering significa que nuestra SPA es básicamente un archivo index.html que a su vez carga un archivo .js donde está toda nuestra aplicación, con todos sus componentes (aunque a veces puede ser más de un archivo .js).

Cuando el browser recibe del servidor estos archivos interpreta el código JavaScript y renderiza la aplicación.

Entonces, lo único que el servidor debe hostear es ese archivo index.html y los acrhivos .js (más los .css). Estos archivos viven en el servidor como si estuviesen en un freezer, porque dentro del servidor no hacen nada, solamente están ahí esperando a recibir una llamada de un browser y el servidor se los envía. Cuando el browser los recibe, los descongela y nuestro frontend cobra vida.

El servidor que hostea una aplicación de frontend no es más que un contenedor que tiene en su memoria estos archivos, como el disco duro de nuestra computadora. Todos los procesos (y todo el consumo de energía y CPU) ocurren en el browser del usuario, no en el servidor.

Por eso, las SPA no necesitan un hosting muy sofisticado. Se puede usar cualquier servicio gratuito como GitHub Pages, o el plan básico de Netlify.

Deploy para un Backend

Muy distinto es el funcionamiento de una aplicación de backend. Una aplicación de backend tiene que estar siempre activa, no puede estar freezada.

Por ejemplo, si el frontend necesita acceder a la base de datos del backend, le envía una petición al backend (mediante un fetch) y el backend tiene que procesar la información y conectarse con la base de datos para obtenerla. Si el backend, por algún motivo, se cae, las peticiones de nuestro frontend van a dar error.

Entonces, hacer un deploy de un backend es bastante más complicado, porque en este caso el servidor no es solamente un contenedor de archivos, es una computadora que procesa información. Para eso se necesita un servicio más sofisticado. En general son servicios pagos, aunque Vercel ofrece un plan gratuito con ciertas limitaciones (Heroku solía tener un plan gratuito pero ya no).

Deploy para SSR

Hoy en día lo que más se suele usar son las aplicaciones híbridas que se ejecutan tanto en el client side (o sea, el browser) como en el server side (o sea, el servidor). Este es el caso de las apps hechas con Next, Nuxt o SvelteKit.

A estas aplicaciones híbridas se les suele llamar SSR (o sea, Server-Side Rendering).

En el caso de Nuxt (el meta-framework de SSR para Vue) el funcionamiento es así: en lugar de tener toda la aplicación en un archivo JavaScript, cuando el servidor recibe una petición en la URL del sitio el servidor compila los archivos .vue y los transforma en archivos .html, que son mucho más livianos que los archivos JavaScript y no necesitan tanto procesamiento por parte del browser. Entonces, cuando esos archivos HTML llegan al browser se muestran en forma instantánea. Eso hace que la carga inicial de la aplicación sea muy rápida.

Pero al no haber JavaScript ese HTML que envió el servidor no tiene interacción, o sea, el usuario ve la página pero aún no puede interactual con ella. Para que haya interacción el servidor envía nuevamente toda la aplicación en formato JavaScript en un proceso que se llama hidratación.

La velocidad de la carga inicial hace que los sitios hechos con SSR tengan mejor posicionamiento en las búsquedas de Google (lo que suele llamarse Search Engine Optimization, o SEO).

Además, cada página HTML contiene meta-data (por ejemplo, en qué categoría o con que hashtags se puede identificar al sitio) que permite que Google pueda clasificarla más fácilmente, lo cual también ayuda a que se posicione mejor en las búsquedas en comparación con los sitios creados con Client-Side Rendering.

Otra ventaja es que los SSR pueden funcionar como un frontend y un backend al mismo tiempo. Por ejemplo, se pueden crear REST APIs del lado del servidor que conecten con una base de datos sin necesidad de tener que crear una aplicación de backend aparte. Y además, esto permite que las aplicaciones SSR sean más seguras. Por ejemplo, las API KEYs secretas pueden estar guardadas del lado del servidor en lugar de quedar expuestas en el browser.

Pero hacer un deploy de una app SSR es mucho más complicado, y quedará para otro post 📝️

Deploy con Vite en GitHub Pages

Hacer un deploy de una aplicación hecha con Vue 3 + Vite en GitHub Pages es bastante fácil, sólo hay que seguir estos pasos:

1. En su proyecto instalan la librería gh-pages como devDependency:

bash
pnpm i -D gh-pages

2. En package.json agregan estos dos scripts: "predeploy" y "deploy".

json
"scripts": {
  "dev": "vite",
  "build": "vite build",
  "preview": "vite preview",
  "predeploy": "npm run build", 
  "deploy": "gh-pages -d dist" 
},

dist es el nombre del directorio donde Vite guarda el build.

Aunque hayan instalado las dependencias con pnpm, para el script deben usar npm. Esto es porque pnpm no permite la ejecución de scripts del tipo pre.

3. Entran al archivo vite.config.js y van a ver algo así:

js
import { fileURLToPath, URL } from 'node:url'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'

export default defineConfig({
  plugins: [vue()],
  resolve: {
    alias: {
      '@': fileURLToPath(new URL('./src', import.meta.url))
    }
  }
})

Al comienzo del método defineConfig agregan la propiedad base (la URL base de nuestro proyecto en GitHub Pages) que debe ser igual al nombre del repositorio:

js
export default defineConfig({
  // URL base en GitHub Pages
  // Es igual al nombre del repo:
  base: '/nombre-del-repo/', 

  // El resto es igual...
})

Al modificar la propiedad base Vite automáticamente cambia el valor de la variable de entorno import.meta.env.BASE_URL. Esta variable es usada por Vue Router para determinar la URL base del proyecto, con lo cual no es necesario modificar la URL base en Vue Router. Con sólo modificar la propiedad base en vite.config.js Vue Router toma ese valor como URL base.

4. Si aún no lo hicieron, inicializan el repositorio en GitHub desde VS Code con Source control y hacen el primer commit:

GitHub Pages

Si ya lo tienen inicializado, hacen un nuevo commit.

Recuerden que primero deben estar loggeados a GitHub dentro de VS Code, con el ícono del usuario de abajo a la izquierda:

GitHub Login

También recuerden que para poder usar GitHub Pages el repositorio debe ser público, no privado.

5. En la terminal ejecutan el comando npm run deploy.

6. Luego entran al repositorio en la página de GitHub, van a la sección Actions y chequean que la acción pages build and deployment se haya completado sin errores:

GitHub Pages Deploy

  1. Luego entran a Settings, Pages:

GitHub Pages

Y ahí les va a aparecer un cartel que dice:

Y listo, ya pueden ver su sitio online 🥳️

Y cada vez que hagan algún cambio, hacen un nuevo deploy con npm run deploy (si no ven los cambios actualizados en la página denle refresh al browser).

URL base condicional

Luego de hacer el deploy a GitHub Pages van a notar que cada vez que usen npm run dev el servidor de desarrollo se inicia en una URL que contiene el nombre del repo:

bash
VITE v3.0.2  ready in 198 ms

    Local:   http://localhost:5173/nombre-del-repo/

Esto es porque cambiamos la URL base en el archivo vite.config.js 😒️

Es un poco molesto que quede así, pero se puede solucionar haciendo que la URL base sólo cambie si estamos en producción, no en desarrollo.

Para por hacer eso el método defineConfig debe exportar un callback que retorna la configuración. Y dentro del return incluir un condicional ternario para base:

js
import { fileURLToPath, URL } from 'node:url'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'

export default defineConfig( ({ mode }) => {

  const dev = mode === 'development' 

  return {
    base: dev ? '/' : '/nombre-del-repo/', 
    plugins: [vue()],
    resolve: {
      alias: {
        '@': fileURLToPath(new URL('./src', import.meta.url))
      }
    }
  }
})

Entonces, si el modo es development (o sea que la app fue iniciada con npm run dev) la URL base es '/', y si estamos haciendo un build para producción la URL base es el nombre del repo ✅️

Hash Routing

Queda solamente un problema: si le dan refresh al browser estando en una ruta que no sea la raíz van a ver que tienen un error 404 (Page Not Found). Esto es porque GitHub Pages no está preparado para funcionar con SPAs.

La forma solucionar esto es usando la función createWebHashHistory de Vue Router:

En el archivo donde configuraron las rutas importan esta función:

js
import { createRouter, createWebHashHistory } from 'vue-router'

Y luego, al crear el objeto del router:

js
const router = createRouter({

  history: createWebHashHistory(import.meta.env.BASE_URL),

  // etc...

}

Luego deben hacer un nuevo commit y un nuevo deploy con npm run deploy. Y listo, al hacer refresh sobre cualquier ruta el browser se reinicia en esa misma ruta ✅️

El único problema es que ahora las rutas van a incluir un símbolo hash:

https://nombre-de-usuario.github.io/nombre-del-repo/#/nombre-de-la-ruta

Este problema lo van a encontar únicamente en GitHub Pages. Si hacen el deploy con Vercel o Netlify no es necesario usar createWebHashHistory, pueden usar createWebHistory, que es la función que incluye Vue Router por default, y las rutas no van a incluir el hash.

GitHub Actions

El único problema con el ejemplo anterior es que, cada vez que hagan algún cambio en el proyecto, por más que hagan un commit, si no usan el comando npm run deploy el sitio en GitHub Pages no se va a actualizar.

Sería mejor si cada nuevo deploy se hiciese automáticamente cada vez que hacemos un nuevo commit.

Esta forma de hacer deploys es lo que se suele llamar Continuous Integration/Continuous Deployment, o CI/CD.

Es un poco más complicada que la anterior pero tampoco es tan difícil:

1. En el repositorio de GitHub van a Settings, Actions, General:

GitHub Actions

2. Abajo de todo seleccionan la opción Read and write permissions y le dan Save:

GitHub Permissions

3. En la raíz del proyecto crean un directorio llamado .github y dentro de éste otro dir llamado workflows. Dentro de workflows crean un archivo llamado deploy.yaml. El archivo puede llamarse de cualquier forma, siempre que la extensión sea .yaml, pero los directorios deben llamarse .github y workflows:

Yaml File

yaml es un formato de archivos parecido a JSON, pero que usa indentación en lugar de llaves para distinguir los bloques de código.

4. Dentro de ese archivo pegan este código. Ésta es una workflow ya preparada para ser usada con Vite y pnpm (si el proyecto no fue creado con Vite y pnpm esta workflow no va a funcionar):

yaml
name: Deploy

on:
  push:
    branches:
      - master

jobs:
  build:
    name: Build
    runs-on: ubuntu-latest

    steps:
      - name: Checkout repo
        uses: actions/checkout@v3

      - name: Setup pnpm
        uses: pnpm/action-setup@v2
        with:
          version: 8

      - name: Setup Node
        uses: actions/setup-node@v3
        with:
          node-version: 18
          cache: pnpm

      - name: Install dependencies
        run: pnpm i

      - name: Build
        run: pnpm build

      - name: Upload production-ready build files
        uses: actions/upload-artifact@v2
        with:
          name: production-files
          path: ./dist

  deploy:
    name: Deploy
    needs: build
    runs-on: ubuntu-latest

    steps:
      - name: Download artifact
        uses: actions/download-artifact@v2
        with:
          name: production-files
          path: ./dist

      - name: Deploy to GitHub Pages
        uses: peaceiris/actions-gh-pages@v3
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          publish_dir: ./dist

5. Luego hacen un nuevo commit con Source Control en VS Code. Y luego van al repositorio en GitHub y entran en la sección Actions:

github actions

6. Ahí chequean que la action se haya completado sin errores:

GitHub Actions no errors

7. Luego chequean que se haya creado una nueva branch llamada gh-pages:

GitHub Pages Branch

8. Luego van a Settings, Pages, seleccionan la branch gh-pages, root, y le dan Save:

GitHub Pages Branch gh-pages

9. Luego vuelven a la sección Actions y chequean que la acción pages build and deployment se haya completado sin errores:

GitHub Pages Deploy

10. Luego vuelven a Settings, Pages y deberían ver un cartel que dice:

Y listo, eso es todo 🥳️

De ahora en más, cada vez que hagan un nuevo commit la página se actualiza automáticamente (si no ven los cambios en la página denle refresh al browser).