materials

Vite

Introducción

Cuando crece el tamaño de un proyecto y tenemos más ficheros de código (cómo al trabajar con clases) es necesario organizarlos de forma que sea fácil encontrarlos y que no haya problemas de dependencias.

Sin embargo tener muchos ficheros hace que tengamos que importarlos todos, y en el orden adecuado, en nuestro index.html (mediante etiquetas <script src="...">) lo que empieza a ser engorroso y reduce el rendimiento al tener el navegador que hacer muchas peticiones HTTP.

Además muchas veces incluimos en el código características del lenguaje que aún no están soportadas por todos los navegadores y cuantos más código usemos más se reduce el número de nevegadores que soportan nuestro código.

Para evitar este problema se utilizan las herramientas de construcción de proyectos o module bundlers que unen todo el código de los distintos ficheros javascript en un único fichero que es el que se importa en el index.html y hacen los mismo con los ficheros CSS.

Además proporcionan otras ventajas:

Nosotros usaremos el bundler Vite que, junto con webpack, son los más usados en entorno frontend. Junto a npm tendremos una forma fácil y práctica de empaquetar el código.

Además Vite incorpora un servidor de desarrollo para hacer más cómoda la creación y prueba de nuestros proyectos.

Para poder usar Vite debemos instalarlo. Como lo usaremos en muchos proyectos lo podemos instalarlo global con

npm install -g vite

Crear un nuevo proyecto

Vite necesita Node.js versión 16 o superior aunque lo mejor es tenerlo actualizado para poder utilitzar todas sus plantillas. Para crear un nuevo proyecto haremos:

npm create vite@latest

(si no tenemos instalado el paquete create-vite nos preguntará si lo instala)

Al crear el proyecto nos pregunta qué framework vamos a utilizar (le diremos que Vanilla, es decir, Javascript sin framework) y si como lenguaje usaremos Javascript o Typescript.

Esto crea el scaffolding de nuestro proyecto que consiste en una carpeta con el mismo nombre que el proyecto y una serie de ficheros en su interior: Nos preguntará el nombre del proyecto, la plantilla (Vanilla para Javascript sin framework) y el lenguaje que queremos usar (Javascript/Typescript) y se crea una carpeta con el nombre de nuestro proyecto que contiene:

Si nuestra aplicación no va a ser muy pequeña como la del ejemplo sino que tendrá diferentes ficheros Javascript, acceso a datos, interfaz de usuario, etc deberíamos organizar en código en carpetas para mejorar su legibilidad. Lo adecuando es dejar en el raíz de nuestro proyecto los ficheros index.html y main.js y crear una carpeta src donde poner el resto de código. En función del tamaño de la aplicación podríamos crear dentro subcarpetas (os propongo un nombre para ellas aunque podéis llamarlas como queráis) para:

NOTA: todos los ficheros javascript de un proyecto con Vite son módulos y en ellos, igual que en las clases, no es necesario poner 'use strict' porque por defecto los módulos ya funcionan así.

Desarrollar nuestro proyecto

Para empezar a trabajar ejecutamos desde la terminal el script

npm run dev

Esto hace que Vite lance un servidor web en el puerto 5173 donde podemos ver la ejecución de nuestro proyecto.

Trabajar con distintos ficheros de código

Una de las razones de usar un bundler es que podemos repartir el código entre varios ficheros de forma que quede más organizado.

Para que un fichero pueda tener acceso a código de otro fichero hay que hacer 2 cosas:

  1. El fichero al que queremos acceder debe exportar el código que desea que sea accesible desde otros ficheros
  2. El fichero que quiere acceder a ese código debe importarlo a una variable

Esto es lo que hacíamos en el ejercicio de la frase para poder pasar los tests y lo que haremos con los ficheros donde declaremos clases.

ES6 nos proporciona 2 formas de exportar/importar código:

Named export

De esta manera puedo exportar tantos elementos (funciones, variables, clases, …) de un fichero como quiera. Por ejemplo, si es un fichero con una única función a exportar:

// Fichero cuadrado.js
export function cuadrado (value) {
  return value * value
}

En el caso de querer exportar muchas funciones lo más sencillo es exportarlas juntas en un objeto como en el fichero functions.js:

function letras () {
  ...
}

function palabras () {
  ...
}

function maysc () {
  ...
}
...
export {
	letras,
	palabras,
	maysc
}

Si es un fichero que define una clase la exportamos tal cual:

export class Product {
    constructor() {

    }
    ...
}

Para importarlo lo hacemos poniendo lo que queremos importar entre { }. Si se trata de una única función:

import { cuadrado } from './cuadrado.js'

console.log('El cuadrado de 2 es ' + cuadrado(2))

También podríamos usar un nombre diferente para lo que importamos:

import { cuadrado as cuad} from './cuadrado.js'

console.log('El cuadrado de 2 es ' + cuad(2))

Si es un fichero con muchas funciones exportadas a un objeto podemos importar sólo las que queramos o todas:

import { letras, maysc } from './functions.js'

console.log('Las letras de "Hola" son ' + letras("Hola"))

También podemos importarlas todas a un objeto sin indicar sus nombres, con *:

import * as MyFunctions from './functions.js'

console.log('Las letras de "Hola" son ' + MyFunctions.letras("Hola"))

Para importar una clase:

import { Product } from './product.class'

const myProd = new Product()

Default export

De esta manera sólo puedo exportar un elemento por fichero, y a la hora de importarlo le doy el nombre que quiera. Por ejemplo, si es un fichero con una única función a exportar:

// Fichero cuadrado.js
export default function cuadrado (value) {
  return value * value
}

Y donde vaya a usarlo:

import myCuadrado from './cuadrado.js'

console.log('El cuadrado de 2 es ' + myCuadrado(2))

En el caso de una clase:

export default class Product {
    constructor() {

    }
    ...
}

Y para importarla:

import Product from './product.class'

const myProd = new Product()

En resumen, si exporto con export importo con { } y si exporto con export default importo sin { } pero sólo puedo exportar 1 elemento.

Imágenes

Si se trata de imágenes estáticas lo más sencillo es ponerlas dentro de la carpeta public y hacer referencia a ellas usando ruta absoluta. Todo lo que está en public se referencia como si estuviera en la raíz de nuestra aplicación:

<img src="/vite.png" height="100px" alt="Logo de Vite">

También podemos poner las imágenes en la carpeta assets, pero antes de usarlas deberemos imnportarlas. Ejemplo:

import viteLogo from './assets/vite.png'
...
const logoHtml = `<img src="${viteLogo}" height="100px" alt="Vite logo">`
document.body.prepend(logoHtml)

Con Vite también podemos importarlas usando import.meta.url, lo que es útil si no conocemos previamente el nombre del fichero con la imagen (más información en la documentación de Vite):

function getImage(fileName, height, alt) {
  imgUrl = new URL(fileName, import.meta.url).href
  const imgHtml = `<img src="${viteLogo}" height="${height}" alt="${alt}">`
  
  return logoHtml
}

Paso a producción

Cuando lo hayamos acabado y queramos subirlo a producción ejecutaremos

npm run build

que crea la carpeta /dist con los ficheros que debemos subir al servidor web de producción:

Testear el proyecto

Si queremos testear el proyecto deberemos usar una herramienta de testing y crear los tests adecuados. Lo más sencillo es usar Vitest que es muy similar a Jest pero preparado para interacutar fácilmente con Vite.

Lo primero es importarlo como dependencia de producción (no lo usaremos en producción)

npm install --save-dev vitest

o abreviado

npm i -D vitest

Debemos añadir un nuevo script en el package.json que le indique a vite que queremos usarlo para testear:

  "scripts": {
		...
    "test": "vitest"
  },

Crearemos los tests en una carpeta en la raíz de nuestro proyecto llamada /test y en ella crearemos los diferentes fichero cuya extensión será .spec.js o .test.js. Cada vez que queramos pasar los tests ejecutaremos

npm run test

Podéis obtener más información en infinidad de páginas de internet, como el Curso DWEC de Jose Castillo, y en la web oficial de vite.