Cuando se habla de Node.js
se traduce en usar Javascript
como lenguaje del lado de servidor, es decir, fuera del navegador. Node.js
es un entorno que corre Javascript
y se construyó con C++
, permitiendo interactuar con el sistema operativo (Windows, macOS, Linux u otro). No obstante, a diferencia de Javascript
para el navegador (o cliente) no se manipula directamente el esquema de HTML
(DOM
), mas bien Node.js
envía los archivos al cliente para que estos se visualicen (distinto es que tales archivos también tengan código de cliente). Por tanto, se logra unificación del lenguaje y ampliación para contemplar, por ejemplo, acceso y manipulación de archivos y bases de datos, tu propio servidor web (http
), micro-servicios web (API-Rest), una interfaz de línea de comandos, o simplemente scripts
en el servidor escritos con Javascript
.
Por otra parte, Node.js
es una de las tecnologías que responden al denominado problema de las diez mil conexiones concurrentes (C10K), es decir, posibilita un buen acceso (muy superior a tecnologías web de una década hacia atrás que al día de hoy aún se encuentran en muchos sistemas y sólo gestionan un promedio de 100 peticiones concurrentes). Al ser ligero y potente, posibilita el uso de infraestructura mínima para ir creciendo o escalando, o bien, no se requiere una inversión inicial abismal en infraestructura para brindar un buen servicio (al decir infraestructura se descarta el servicio de alojamiento web convencional puesto que requiere alguna máquina, al menos VPS o servicio especializado).
Actualmente también se requiere
Node.js
para gestionar aplicaciones deJavascript
(aunque se trate de una aplicacion para el navegador o UI).
Para programar con Javascript
moderno (ES6/ES2015+), bien sea del lado del cliente o del servidor (con Node.js
) se requiere al menos lo siguiente:
Node.js
para el sistema operativoSobre el último punto y salvo que se piense en algo distinto a la web, resulta significativo diferenciar entre servicios de alojamiento web convencional (web hosting) y máquinas (o infraestructura), dado que Node.js
no funciona en el primero que es dominado por el lenguaje PHP
. Sin embargo, curiosamente el lenguaje Javascript
moderno ha evolucionado de un modo tan interesante dominando en el software cliente (o navegador) que nos hace pensar en la posibilidad de volver a los orígenes de páginas y sitios construidos con el estándar HTML
y compañía (CSS
y Javascript
) que es lo que se requiere para el diseño web (sin otro lenguaje), lo que se conoce como páginas o sitios web estáticos (archivos que se entregan directamente al navegador). Comprendiendo que estamos hablando de las posibilidades actuales para páginas o sitios web que contienen información para públicar en Internet, cuando se piensa en aplicaciones también es posible salvo que suele requerirse de un lenguaje o entorno que opere en el servidor cuando se trata de involucrar datos y formularios (que genralmente requiren cierta dinámica).
Si esto resulta nuevo para tí, es de comprender que la arquitectura web que venía trabajándose de modo clásico proporcionaba páginas web dinámicas en relación a que en el servidor se procesaba algo semejante a plantillas HTML
mezcladas con código (ejemplo: PHP
, JSP
, ASP
) generando un resultado dinámico para mostrarlo en el navegador, sin embargo, al no tratarse de una arquitectura bidireccional requería una petición y una respuesta en cada ocasión, implementándose página a página dónde la interacción con el usuario sería menos fluida (esto se conoce ahora como “Multiple Pages Application” - MPA
).
Actualmente existe un panorama en el que pueden coexistir diversas arquitecturas o doble dinamismo gracias a Javascript
moderno, aunque el código para el cliente se relacione con el término estático dado que se entrega directamente al navegador tal como se encuentra en el servidor pero al llegar al navegador se vuelve reactivo. No obstante si haces una aplicación nueva debe evaluarse lo que es más eficiente para cada caso. Por ejemplo, si procesas plantillas para “emails” podría ser interesante recurrir a un estilo clásico pero si quieres prestar un mejor servicio o diseñar micro-servicios están tecnologías como Node.js
. Como generalmente se evoluciona, cabe aclarar que se hablaba de cierta arquitectura dominada por la popularidad de PHP
en el contexto digital, lenguaje que actualmente también posibilita implementar micro-servicios con proveedores que te ofrecen algunos dólares menos que lo que cuesta el servicio de una máquina virtual pequeña (orientadas a recurso técnico o desarrollador con conocimientos de Linux en un nivel apropiado), pero volviendo a nuestro asunto de preparar un entorno de desarrollo para Javascript
deberemos enfocarnos en este lenguaje.
Javascript es en principio síncrono pero puede resolver sentencias asíncronas, ¿y cómo se explica esto? Gracias al loop de eventos que es un bucle que verifica ciertos criterios contando con ciertas características: La pila de llamadas (call-stack), la cola de tareas (task-queue), y Web-API’s como timers y funciones de red, principalmente. Veamos a modo general el flujo:
setTiemeOut
, setInterval
) se delega esto a la API respectiva, igualmente para funciones de red. Cuando una Web API es procesada se traduce como tarea asíncrona y finalmente coloca las funciones asociadas en la cola de tareas.Esto se hace en medio de un ciclo sin interrumpción (hasta cancelación el proceso) y mediante un hilo de procesos.
Como es usual en Windows, simplemente te diriges al sitio del fabricante (https://nodejs.org/, etc.) y descargas el instalador. Sin embargo, a continuación se indica la instalación bajo línea de comandos con privilegios de administrador si usas el gestor de paquetes Chocolatey. Básicamente ejecutas choco install nodejs-lts -y
. Veamos entonces cómo aprovisionamos el ambiente con las herramientas citadas anteriormente.
choco install nodejs-lts -y
choco install vscode -y
choco install googlechrome -y
choco install git -y
La última línea sólo indica la conveniencia de instalar Git (un controlador de versiones) en caso de llegar a usar una herramienta que incopora una consola de comandos tipo Linux, denominada Git Bash.
Básicamente bajas el paquete y ejecutas sudo apt install -y nodejs
desde la línea de comandos. Veamos entonces cómo aprovisionamos el ambiente con las herramientas citadas anteriormente.
curl -sL https://deb.nodesource.com/setup_10.x | sudo -E bash -
sudo apt install -y nodejs
sudo apt install -y build-essential
sudo snap remove vscode
sudo snap install code --classic
wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
sudo apt install -y libxss1 libgconf2-4 libappindicator1 libindicator7
sudo dpkg -i google-chrome-stable_current_amd64.deb
rm google-chrome-stable_current_amd64.deb
Bastarían las dos primeras líneas para lo referente a
Node.js
, quizás la tercera para ciertos casos requeridos por paquetes avanzados. Las demás líneas corresponden al editor y al navegador.
Abre un archivo de extensión .js
, por ejemplo index.js
, y coloca el siguiente código:
console.log('Hi there!')
Para ejecutarlo, haz lo siguiente:
node index.js
Si hacemos un ejercicio en versión web, se necesitaría el siguiente código:
var http = require('http');
http.createServer((req, res) => {
res.write('Hi there!');
res.end();
}).listen(8080);
En este caso, se indica que se requiere el módulo
http
y luego se invoca el métodocreateServer
dónde se procesa el mensaje, creando el servicio web para ser escuchado (listen
) en el puerto8080
. En unas pocas línas tenemos un servidor web que podemos correr con la sentencianode index.js
. Puede consultarse en un navegador usando la direcciónlocalhost:8080
.
Generalmente al instalar Node.js
viene el gestor de paquetes y módulos conocido como npm
, así que para iniciar nuestro proyecto abrimos una carpeta nueva (por ejemplo, test
) y ejecutamos dentro de ésta lo siguiente:
npm init
A las preguntas que hace esta sentencia, por ahora lo mas relevante es identificar el punto de entrada (enty point) que será nuestro
script
principal, por ejemplo,index.js
, y se confirma afirmativamente a la última pregunta sobre si está “OK”. Esto creará un archivo de configuración de paquetes denominadopackage.json
.
Se puede evitar usar npm init
y en su lugar escribir nuestro archivo package.json
indicando la dependencia de restana
con el conocimiento respectivo, luego se ejecutaría npm install
. En todo caso, si vemos éste archivo de configuración de paquetes debería asemejarse al siguiente contenido:
{
"name": "test",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"restana": "^4.9.9"
}
}
El marco de trabajo express
es reconocido para implementar un servidor web en Node.js
y gestionar sus peticiones en lugar de usar el modo nativo. Veamos un ejemplo de inicio sobre como establecer un servidor web bajo la propuesta de esta tecnología.
const express = require('express'); // import the module for Node.js
const service = express();
service.get('/', (req, res) => { // initial function
res.send('Express says: Hi there!');
});
const server = service.listen(3000); // open the server
Debe instalarse el módulo y luego lanzas el servicio, suponiendo que el archivo se nombra como index.js
se ejecuta cada uno de los siguientes comandos:
npm install express -S
node index.js
Para crear un servicio Web, usaremos un módulo publicado en npm
denominado restana
(gracias a Rolando Santamaria Maso). Los ejemplos convencionales suelen usar express
que es el framework popular, pero éste lo recomiendo por tratarse de una librería ligera enfocada en API’s que tiene un tiempo de respuesta mejor y simpatiza con algunos aspectos esenciales de express
. Lo instalamos de la siguiente manera:
npm install restana -S
Ahora abrimos un nuevo archivo para nuestro programa con nombre server.js
y colocamos (copiar y pegar) el siguiente contenido:
const service = require('restana')(); // import the module for Node.js
service.get('/', (req, res) => { // initial function
res.send('Restana says: Hi there!');
});
service.start(3000); // start the server
Con tan sólo estas simples líneas, hemos elaborado un servicio http
propio para ser escuchado en un puerto (3000). Después de guardarlo se invoca desde la línea de comandos de la siguiente manera:
node server.js
Se debe abrir un navegador y consultar la dirección
http://localhost:3000
. En este caso no necesariamente se trata de una página web sino de un servicio web que se encuentra disponible para ser consumido o requerido por otro servicio, o en nuestro caso un usuario. Usualmente lo que debe retornar es unJSON
que no corresponde al nivel de usuario sino a un nivel de aplicación o información que puede ser procesada por unaapp
en el navegador.
Es posible modificar el archivo package.json
para indicar el comando de script
de inico de nuestro programa servidor. Comprendiendo lo anterior, se agrega start
bajo scripts
y ese bloque quedaría así:
"scripts": {
"start": "node server.js",
"test": "echo \"Error: no test specified\" && exit 1"
},
De este modo, se puede ejecutar de modo genérico el comando:
npm start
Ahora vamos a mejorar nuestro ejercicio dejando el contenido de server.js
de la siguiente manera:
const service = require('restana')();
service.get('/', (req, res) => {
res.send('Hi there!');
});
service.get('/yourname/:name', (req, res) => {
res.send({ "yourname": req.params.name });
});
// start the server
service.start(3000).then((server) => {
console.log(`Service listening as ${server._connectionKey}`);
});
Nótese que en la constante
service
se recibe el módulo o librería importándola conrequire
(en lugar deimport
), de modo que en adelante podemos usar sus funciones. En este caso usamosget
para incorporar la rutayourname
que recibe como parámetroname
y reporta una respuestaJSON
. De este modo puedes consultarla en la barra de direcciones del navegador agregando/yourname/john
, dónde “john” es el nombre enviado. Adicionalmente dejamos un mensaje en consola sobre el servicio en ejecución.
Debes interrumpir el proceso anterior (CTRL-C
) y lanzarlo de nuevo (node server.js
), luego refrescas el navegador.
Para finalizar, en este tipo de servicio es posible encadenar el flujo de solicitudes web con el uso de next()
. Asegurate de que el archivo contenga ahora el siguiente código y recuerda reiniciar el servicio.
const service = require('restana')();
function audit (req, res, next) {
console.log(req.url);
next(); // Next middleware
}
service.get('/', (req, res) => {
res.send('Hi there!');
});
service.get('/yourname/:name', audit, (req, res, next) => {
if (req.params.name.length < 3)
return res.send({ status: 'error', message: 'invalid name' });
res.send({ yourname: req.params.name });
});
// start the server
service.start(3000).then((server) => {
console.log(`Service listening as ${server._connectionKey}`);
});
Aunque no se trate de un ejemplo cercano a la realidad (pues no se debería saturar el uso de
console.log
), puede observarse que la diferencia más significativa consiste en el uso de la funciónaudit
que invoca anext()
. Lo que se intenta ilustrar es que pueden existir bloques de lógica independientes (middleware) que pueden reutilizarse, incluso incorporar un servicio de terceros según los momentos del flujo (por ejemplo, enviar un email). Por otra parte, agregamos una validación para dar una respuesta con un mensaje de error cuando el nombre tiene menos de 3 caracteres.
Ahora reorganicemos el código en una nueva versión quedando con el siguiente contenido:
const fs = require('fs');
const service = require('restana')();
const port = 3000;
const audit = (req, res, next) => {
console.log(req.url);
next();
}
const names = (req, res, next) => {
if (req.params.name.length < 3)
return res.send({ status: 'error', message: 'invalid name' })
res.send({ yourname: req.params.name });
}
const files = (res, file) => {
fs.readFile(file, 'utf8', (err, data) => {
if (err)
return res.send(err);
return res.end(data);
});
}
service.get('/', (req, res) => { files(res, './web/index.html') });
service.get('/yourname/:name', audit, names);
service.start(port).then((server) => {
console.log(`Service listening on ${port}`);
});
Observa los cambios. Lo más significativo es el uso del módulo
fs
(nativo de Node.js, por eso no se instala) para gestionar archivos, en este caso para leer./web/index.html
por lo que debe existir o crearse con algún contenido (de lo contrario reporta error). Además, se reorganizó el código usando funciones tipoarrow
que es otra manera de expresarlas, así como también se incorpora una constante para el puerto del servicio.
En cuanto al uso de archivos estáticos debería exponerse una carpeta destinada para ello (web
) y no un archivo. Para esto ya existe el módulo serve-static
que se puede instalar (npm install serve-static -S
), y al combinarlo con restana
tendríamos finalmente el siguiente código:
const files = require('serve-static');
const service = require('restana')();
const port = 3000;
audit = (req, res, next) => {
console.log(req.url);
next();
}
names = (req, res, next) => {
if (req.params.name.length < 3)
return res.send({ status: 'error', message: 'invalid name' });
res.send({ yourname: req.params.name });
}
service.use(files('./web'));
service.get('/yourname/:name', audit, names);
service.start(port).then((server) => {
console.log(`Service listening on ${port}`);
});
Nótese que, además de importar el nuevo módulo, el cambio se centra en utilizar
service.use(files('./web'))
, dóndeuse
indica que debe ejecutarse la gestión defiles
, que ahora corresponde al servicio que expone la carpeta de archivos estáticos.
Podemos combinar la anterior tecnología (restana
) para potenciarla con low-http-server
y obtener un tiempo de respuesta superior. Para instalarlo ejecutamos lo siguiente:
npm install low-http-server -S
Y como ejemplo correspondiente para un servidor web, tendríamos un código como el siguiente:
const server = require('low-http-server')({});
const service = require('restana')({
server: server,
prioRequestsProcessing: false // for great performance
});
const port = 3000;
service.get('/', (req,res) => {
res.send('Hi there!');
});
server.listen(3000, () => {
console.log(`Service listening on ${port}`);
});
low-http-server
esta basado enuWebSockets
que brinda un mejor desempeño respecto al modo convencional de operar enNode.js
.
Una de las ventajas de usar Express se debe su gestión de rutas para orgonizar la aplicación. Lo que se busca es que cuando se repite un patrón de ruta se puede usar route
y encadenar el verbo http
respectivo. Veamos el ejemplo:
const express = require('express'); // import the module for Node.js
const service = express();
service.route('/thing')
.get((req, res) => {
res.send('GET thing');
})
.post((req, res) => {
res.send('POST thing');
});
const server = service.listen(3000); // open the server
Nótese que se usa
route
y se encadenaget
ypost
Sin embargo, podemos usar nuestra aplicación con la parte que expresa la ruta y modularizar las rutas en otro archivo gracias a Router
. De este modo, nuestro archivo principal se vería así:
const express = require('express'); // import the module for Node.js
const service = express();
const thing = require('./thing');
service.use('/thing', thing);
const server = service.listen(3000); // open the server
Y en un nuevo archivo (thing
) tendríamos el siguiente contenido:
const express = require('express'); // import the module for Node.js
const router = express.Router();
router('/')
.get((req, res) => {
res.send('GET thing');
})
.post((req, res) => {
res.send('POST thing');
});
module.exports = router;
En este archivo no se incluye la ruta
/thing
sino las subrutas correspondientes (/...
) usando la instancia deRouter
y el verbohttp
.
De este modo, se puede hacer un CRUD según el módulo que consistiría en un archivo separado. Además, se pueden incluir rutas con parámetros o expresiones regulares.
Tambien podría usarse express-generator
para agilizar la creación de una aplicación con rutas ejecutando, por ejemplo, los siguientes comandos:
npm i express-generator -g
express --no-view project
cd project
npm install
npm start
Para ejecutar lo anterior debe existir una carpeta destinada como espacio de trabajo de proyectos de código, de modo que nos ubicamos dentro de ella desde la línea de comandos.
project
correspondería al nombre asignado para el proyecto.
Además de modulos como express
encontramos socket.io
que es reconocido para implementar websockets usados en Node.js
para aplicaciones en tiempo real, como es caso de un “chat”. Veamos un ejemplo de inicio sobre como establecer websockets con esta tecnología.
const server = require('http').createServer(); // Web server
const io = require('socket.io')(server); // import the module for Node.js
io.on('connection', (browser) => {
browser.on('my-event', (data) => { // event from the browser/client
console.log('Hi there!');
});
browser.on('disconnect', () => {
console.log('Bye!');
});
});
server.listen(3000); // open the server
Debe instalarse el módulo y luego lanzas el servicio, suponiendo que el archivo se nombra como index.js
se ejecuta cada uno de los siguientes comandos:
npm install socket.io -S
node index.js
Un ejercicio completo implementaría un código para una aplicación en el navegador que invoca el evento usando también
socket.io
como cliente.
En el siguiente ejemplo se ilustra la lógica que se puede implementar para usar un cluster con Node.js. Dado que Node.js corre en un sólo hilo, con cluster es posible levantar un proceso por cada procesador, identificando uno maestro. Veamos el ejempplo:
const http = require('http');
const cluster = require('cluster');
const cpus = require('os').cpus().length;
if (cluster.isMaster) {
for (var i = 0; i < cpus; i++)
cluster.fork();
cluster.on('exit', (worker, code, signal) => {
console.log('pid ' + worker.process.pid + ' bye.');
});
}
else
http.createServer((req, res) => {
res.send(`Hi there!\n`);
}).listen(8000);
cluster.isMaster
permite identificar si se trata del proceso maestro sobre el cual se derivan los otros procesos (hilos) que se crearían concluster.fork()
. Mediantecluster.on
es posible capturar el evento de salida (exit
) de cada proceso.
Mientras un cluster utiliza multiples procesos en Node.js, los hilos de trabajos (Worker Threads) posibilitan operaciones paralelas dentro de un solo proceso compartiendo la memoria. Veamos el ejempplo:
const { Worker, isMainThread, parentPort } = require('worker_threads');
if (isMainThread) {
const wkr = new Worker(__filename);
wkr.on('message', msg => {
console.log(`Worker says: ${msg}`);
});
wkr.on('error', error => {
console.error(error);
});
wkr.on('exit', exitCode => {
if (exitCode !== 0)
console.error(`The worker got end with ${exitCode}`);
});
}
else
parentPort.postMessage('Hi, this is the worker');
Se puede identificar si se cumple
isMainThread
, permitiendo capturar los eventos paramessage
,error
yexit
parentPort.postMessage
es usando desde elworker
para enviar mensajes o eventos al hilo principal.
Node.js
provee un conjunto de librerías con métodos que convienen conocer. Las librerías nativas que podemos mencionar inicialmente son fs
para gestionar archivos, path
para el manejo de ubicaciones locales, url
para el manejo de rutas web, y http
para el servicio web (aunque hemos usado en su lugar una libería no nativa de nombre restana
), entre otras.
Método | Descripción |
---|---|
require |
Importa librerías (o archivos) tanto nativas como de terceros. En nuevas versiones es posible usar import (con herramienta webpack que veremos avanzando el texto) |
__dirname |
Variable del entorno que devuevle el directorio desde donde se ejecuta el entorno. |
console.log |
Al igual que javascript del lado del navegador, es usado para imprimir salida de consola (en este caso, en la pantalla). |
fs.readFile |
Lee un archivo |
fs.writeFile |
Escribe un archivo |
path.join |
Concatena rutas con el separador apropiado para el sistema |
url.format |
Formatea una url apropiadamente basándose en parámetros (protocolo, ruta, slashes) |
http.createServer |
Inicia un servidor para peticiones web (http) |
Existe un objeto en Node.js denominado process
, el cual nos permite acceder a ciertas características. Veamos algunos ajemplos:
process.env
: Accede a las variables de entorno activas en el sistema.process.argv
: Nos permite acceder a argumentos recibidos desde la consola (por ejemplo, para una cli
). Los argumentos propios empezarían en la posición 2.process.on('exit', ...)
: Se trata de un evento que ocurre al intentar salir del proceso que se está corriendo.process.stdout.write
: Método que permite escribir en la salida del proceso (también encontramos stdin
)Comprendiendo que lo que llega al navegador sería el cliente y lo que corre fuera del navegador (bajo Node.js) lo designamos como servidor, podríamos citar algunas diferencias claves sobre este asunto.
Concepto | JS para cliente | JS para servidor |
---|---|---|
Acceso al DOM (HTML) | Sí | No |
Setencia FETCH para peticiones web | Sí | No |
Gestión de archivos de modo nativo | No | Sí |
Acceso a bases de datos convencionales | No | Sí |
Acceso a base de datos local para navegador | Sí | No |
Servicio Web o de páginas HTML | No | Sí |
Renderizado desde el servidor - SSR | No | Sí |
API de microservicios Web | No | Sí |
Aplicaciones de línea de comandos - CLI | No | Sí |
Scripts para servidor | No | Sí |
Aplicaciones para el escritorio - PWA | Sí | No |
Algún punto puede ser considerable dado que existen tecnologías como
Electron
oNW.js
que integran ambas partes posibilitando justamente aplicaciones para el escritorio, también denominadas aplicaciones web progresivas (PWA).
Si se piensa que existen unos archivos de orígen que pueden ser organizados obteniendo unos archivos distribuibles algo distintos se requiere de algún mecanismo que lo facilite. Esta es la manera de trabajar que se propone para el desarrollo web moderno. Por tanto, dejan de usarse scripts
y otros archivos de modo directo. Siendo así, aunque un script
corresponda al lado del cliente, en tiempo de desarrollo se utilizaría Node.js
y sus facultades. De hecho, bajo esta filosofía de desarrollo para componentes de naturaleza estática, aunque se use PHP
, Python
, Java
u otro lenguaje del lado del servidor, se usaría igual Node.js
para el entorno de desarrollo.
Webpack
es una herramienta que opera bajo Node.js
simplificando las tareas de empaquetamiento de archivos distribuibles sin necesidad de un gestor de tareas (podemos citar gulp
, también se pueden combinar), es decir, hace posible organizar la entrega de programas generalmente relacionados con archivos html
, css
, js
. Adicionalmente ofuzca el código y dadas las diferentes versiones de Javascript
(es5, es6, es7, etc.) se aplica la transpilación (término usado como sinónimo de transformación considerando que tampoco es compilación) a la versión distribuible conforme a los navegadores objetivo (por ejemplo los modernos: edge, chrome 80+, firefox 70+, safari 10+), así como otro tipos de lenguajes que transpilan a js
, como por ejemplo Typescript
(ts), Dart
, Kotlin
, y librerías como React
(jsx) y Riot
, entre muchas otras tecnologías.
En el contexto de desarrollo moderno con Javascript
, es indispensable una herrammienta como ésta o similar (por ejemplo, ParcelJS
), siendo actualmente Webpack
la más popular (teniendo acogida para ser usado con otras tecnologías como el gestor de tareas Gradle
en la JVM).
Para empezar se debe contar con una carpeta que corresponda al proyecto de ejemplo y en la que se encuentra el archivo package.json
. Para nuestro ejercicio con Webpack
instalaremos lo siguiente:
npm install webpack webpack-cli -D
Nótese que se usa
-D
dado que son módulos útiles en tiempo de desarrollo y no como dependencia final.
Debemos reorganizar la estructura del proyecto para distinguir nuestros fuentes del código generado por Webpack
como destino. Como estándar para los fuentes se busca una carpeta src
, de modo que allí debe quedar nuestro archivo Javascript
.
Además, se modifica el archivo package.json
para indicar el comando de script
de construcción de nuestro programa. Comprendiendo lo anterior, se agrega build
bajo scripts
y ese bloque quedaría así:
"scripts": {
"start": "node server.js",
"build": "webpack --mode development",
"build:prod": "webpack -p --progress",
"test": "echo \"Error: no test specified\" && exit 1"
},
De este modo, se podría ejecutar el comando:
npm run build
En un escenario sencillo, Webpack
funciona sin configuración asumiendo que encuentra un archivo src\index.js
y lo distribuye por defecto como dist\main.js
, como se puede observar a continuación:
📂 project
├── src
│⋅⋅⋅⋅⋅⋅└── index.js
├── dist
│⋅⋅⋅⋅⋅⋅└── main.js
└── index.html
Sin embargo, para especificar algunas cosas o personalizarlo es usual el uso de un archivo de configuración denominado por defecto webpack.config.js
. Procedemos a inicializarlo (creándolo en un editor) con el siguiente contenido:
const path = require('path');
module.exports = {
entry: path.join(__dirname, 'src', 'index.js'),
output: {
filename: 'main.js',
path: path.join(__dirname, 'web', 'dist')
},
module: {
rules: [
{
exclude: /node_modules/,
},
],
},
}
Como se puede observar, se usa un archivo JS
que exporta un objeto de módulo, el cual tiene principalmente cinco elementos esenciales que son: entry
, output
, mode
, module
, plugins
.
Este archivo de ejemplo utiliza los elementos
entry
,output
ymodule
(aplicando una regla para excluir los módulos Node.js). Enentry
se reporta el punto de entrada que se trata de unscript
principal, mientras enoutput
se indica el destino para su distribución. Adicionalmente, en este caso se usa la variable de entorno__dirname
y el módulopath
(nativos de Node.js) para garantizar las ubicaciones (aplicando el separador respectivo para el sistema operativo).
Para comprender mejor la disposición de nuestros archivos, tendríamos lo siguiente:
📂 project
├── src
│⋅⋅⋅⋅⋅⋅└── index.js
├── web
│⋅⋅⋅⋅⋅⋅├── index.html
│⋅⋅⋅⋅⋅⋅└── dist
│⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅└── main.js
├── package.json
└── webpack.config.js
Archivo | Descripción |
---|---|
./package.json |
Archivo de configuración de proyecto con Node.js |
./webpack.config.js |
Archivo de configuración de Webpack |
./src/index.js |
“script” original del lado del cliente |
./web/dist/main.js |
“script” que se generaría con Webpack como versión distribuible de index.js |
./web/index.html |
Plantilla de página web que usaría el “script” distribuible (main.js ) |
Podemos agregar en esta lista el servicio web creado en un ejercicio anterior (
./server.js
), sólo que se ilustra de este modo para distinguir lo que corresponde al cliente del código para el servidor, que en nuestro caso también es bajoNode.js
.
Ahora agreguemos nuestro archivo src/index.js
con el siguiente contenido:
onSend = (e) => {
e.preventDefault();
let input = document.querySelector('input');
let uri = 'http://localhost:3000/yourname/' + input.value;
if (!input.value)
uri = 'http://localhost:3000/yourname/john';
fetch(uri)
.then(res => res.json())
.then(data => {
let holder = document.getElementById('holder')
if (holder) holder.remove();
let code = document.querySelector('code');
code.innerHTML = JSON.stringify(data, null, ' ');
hljs.highlightBlock(code); // use highlight.js
})
.catch(err => {
document.querySelector('code').innerHTML = `${uri} => ${err.message}`;
});
}
De modo separado, abrimos nuestra plantilla web/index.html
con el siguiente contenido:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Template with Semantic-UI styles</title>
<!-- Semantic-UI Styles -->
<link href="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.5.0/semantic.min.css" rel="stylesheet" media="screen">
<!-- HighlightsJS -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.15.9/styles/ocean.min.css">
</head>
<body>
<div class="ui text container">
<br/>
<form onsubmit="onSend(event)">
<div class="ui labeled action fluid input">
<div class="ui label">http://localhost:3000/yourname/</div>
<input type="text" placeholder="john">
<button class="ui icon button">
<i class="search icon"></i>
</button>
</div>
</form>
<div class="ui placeholder segment" id="holder">
<div class="ui icon header">
<i class="search blue icon"></i>
Try it!
</div>
</div>
<pre>
<code style="max-height: 300px; overflow-y: scroll;"></code>
</pre>
</div>
<script src="https://code.jquery.com/jquery-3.6.1.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.5.0/semantic.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.15.9/highlight.min.js"></script>
<script defer src="dist/main.js"></script>
</body>
</html>
Ejecutamos el comando npm run build
(o npm run build:prod
) e iniciamos nuestro servidor (con el comando node server.js
o npm start
). Si todo esta bien, nos dirigimos al navegador y revisamos nuestra aplicación web en el puerto respectivo.
Para un siguiente ejercicio podríamos ver interactuar Webpack
con una librería como Riot
en su versión 5, así continuamos avanzando en el mundo Javascript
.
© 2019 by César Arcila