Diseño y construcción de una aplicación web progresiva sin un marco (Parte 1)

Publicado: 2022-03-10
Resumen rápido ↬ No necesita ser un graduado en Ciencias de la Computación o conocer un marco de JavaScript para crear una Aplicación Web Progresiva. Con algunos conocimientos de HTML y CSS y competencia básica con JavaScript, tiene todas las habilidades que necesita. En tres partes, vamos a compartir el viaje de diseño y construcción de una aplicación web progresiva simple llamada 'In/Out', construida sin un marco. Puedes verlo aquí.

¿Cómo funciona realmente una aplicación web? No me refiero desde el punto de vista del usuario final. Me refiero en el sentido técnico. ¿Cómo se ejecuta realmente una aplicación web? ¿Qué inicia las cosas? Sin ningún código repetitivo, ¿cuál es la forma correcta de estructurar una aplicación? En particular, una aplicación del lado del cliente donde toda la lógica se ejecuta en el dispositivo de los usuarios finales. ¿Cómo se gestionan y manipulan los datos? ¿Cómo hace que la interfaz reaccione a los cambios en los datos?

Estos son el tipo de preguntas que son fáciles de esquivar o ignorar por completo con un marco. Los desarrolladores buscan algo como React, Vue, Ember o Angular, siguen la documentación para ponerse en marcha y listo. Esos problemas son manejados por la caja de trucos del marco.

Eso puede ser exactamente como quieres las cosas. Podría decirse que es lo más inteligente que puede hacer si desea construir algo con un estándar profesional. Sin embargo, con la magia abstraída, nunca puedes aprender cómo se realizan realmente los trucos.

¿No quieres saber cómo se hacen los trucos?

Yo hice. Entonces, decidí intentar construir una aplicación básica del lado del cliente, sin marco, para comprender estos problemas por mí mismo.

Pero, me estoy adelantando un poco; un poco de historia primero.

Antes de comenzar este viaje, me consideraba muy competente en HTML y CSS, pero no en JavaScript. Como sentí que había resuelto satisfactoriamente las preguntas más importantes que tenía sobre CSS, el próximo desafío que me propuse fue comprender un lenguaje de programación.

El hecho era que tenía un nivel relativamente principiante con JavaScript. Y, aparte de piratear el PHP de Wordpress, tampoco tuve exposición ni formación en ningún otro lenguaje de programación.

Permítanme calificar esa afirmación de 'nivel principiante'. Claro, podría hacer que la interactividad funcione en una página. Alternar clases, crear nodos DOM, agregarlos y moverlos, etc. Pero cuando se trataba de organizar el código para cualquier cosa más allá de eso, no tenía ni idea. No estaba seguro de construir nada parecido a una aplicación. No tenía idea de cómo definir un conjunto de datos en JavaScipt, y mucho menos manipularlo con funciones.

No entendía los "patrones de diseño" de JavaScript: enfoques establecidos para resolver problemas de código que se encuentran con frecuencia. Ciertamente no tenía idea de cómo abordar las decisiones fundamentales de diseño de aplicaciones.

¿Alguna vez has jugado 'Top Trumps'? Bueno, en la edición para desarrolladores web, mi tarjeta se vería así (puntos sobre 100):

  • CSS: 95
  • Copiar y pegar: 90
  • Línea del cabello: 4
  • HTML: 90
  • Escritura Java: 13

Además de querer desafiarme a mí mismo a nivel técnico, también me faltaban habilidades de diseño.

Con la codificación casi exclusiva de los diseños de otras personas durante la última década, mis habilidades de diseño visual no habían tenido ningún desafío real desde finales de los años noventa. Reflexionar sobre ese hecho y mis insignificantes habilidades de JavaScript, cultivó una creciente sensación de insuficiencia profesional. Era hora de abordar mis defectos.

Un desafío personal tomó forma en mi mente: diseñar y construir una aplicación web JavaScript del lado del cliente.

¡Más después del salto! Continúe leyendo a continuación ↓

sobre el aprendizaje

Nunca ha habido mejores recursos para aprender lenguajes de computación. Particularmente JavaScript. Sin embargo, me tomó un tiempo encontrar recursos que explicaran las cosas de una manera que encajara. Para mí, 'You Don't Know JS' de Kyle Simpson y 'Eloquent JavaScript' de Marijn Haverbeke fueron de gran ayuda.

Si está comenzando a aprender JavaScript, seguramente necesitará encontrar sus propios gurús; personas cuyo método de explicación funciona para usted.

Lo primero que aprendí fue que no tiene sentido tratar de aprender de un maestro/recurso que no explica las cosas de una manera que entiendes. Algunas personas miran ejemplos de funciones con foo y bar e instantáneamente asimilan el significado. Yo no soy una de esas personas. Si tampoco lo eres, no asumas que los lenguajes de programación no son para ti. Simplemente pruebe con un recurso diferente y siga tratando de aplicar las habilidades que está aprendiendo.

Tampoco es un hecho que disfrutará de cualquier tipo de momento eureka en el que todo de repente "haga clic"; como el equivalente en código del amor a primera vista. Es más probable que se necesite mucha perseverancia y una aplicación considerable de sus aprendizajes para sentirse seguro.

Tan pronto como te sientas un poco competente, tratar de aplicar tu aprendizaje te enseñará aún más.

Aquí hay algunos recursos que encontré útiles en el camino:

  • Canal de YouTube Fun Fun Fun
  • Cursos de Vista Plural de Kyle Simpson
  • JavaScript30.com de Wes Bos
  • JavaScript elocuente por Marijn Haverbeke

Correcto, eso es prácticamente todo lo que necesitas saber acerca de por qué llegué a este punto. El elefante ahora en la habitación es, ¿por qué no usar un marco?

Por qué no reaccionar, Ember, Angular, Vue y otros

Si bien se aludió a la respuesta al principio, creo que el tema de por qué no se usó un marco debe ampliarse.

Hay una gran cantidad de marcos JavaScript de alta calidad y bien soportados. Cada uno diseñado específicamente para la construcción de aplicaciones web del lado del cliente. Exactamente el tipo de cosa que estaba buscando para construir. Te perdono por preguntarte lo obvio: como, err, ¿por qué no usar uno?

Aquí está mi postura al respecto. Cuando aprendes a usar una abstracción, eso es principalmente lo que estás aprendiendo: la abstracción. Quería aprender la cosa, no la abstracción de la cosa.

Recuerdo haber aprendido algo de jQuery en el pasado. Si bien la encantadora API me permitió hacer que las manipulaciones de DOM fueran más fáciles que nunca, me volví impotente sin ella. Ni siquiera podía cambiar de clase en un elemento sin necesidad de jQuery. Encárgueme algo de interactividad básica en una página sin jQuery para apoyarme y me tropecé en mi editor como un Samson rapado.

Más recientemente, mientras intentaba mejorar mi comprensión de JavaScript, traté de entender un poco Vue y React. Pero, en última instancia, nunca estuve seguro de dónde terminaba JavaScript estándar y comenzaba React o Vue. Mi opinión es que estas abstracciones valen mucho más la pena cuando comprendes lo que están haciendo por ti.

Por lo tanto, si iba a aprender algo, quería entender las partes centrales del idioma. De esa manera, tenía algunas habilidades transferibles. Quería conservar algo cuando el marco actual del sabor del mes se había dejado de lado para la próxima 'cosa nueva y caliente'.

Bueno. Ahora, estamos al tanto de por qué se creó esta aplicación y también, nos guste o no, cómo se haría.

Pasemos a lo que esta cosa iba a ser.

Una idea de aplicación

Necesitaba una idea de aplicación. Nada demasiado ambicioso; No tenía ningún delirio de crear una nueva empresa o aparecer en Dragon's Den: aprender JavaScript y los conceptos básicos de la aplicación era mi objetivo principal.

La aplicación tenía que ser algo que tuviera la oportunidad de lograr técnicamente y hacer un trabajo de diseño medio decente para arrancar.

Tiempo tangente.

Fuera del trabajo, me organizo y juego fútbol sala siempre que puedo. Como organizador, es un fastidio anotar mentalmente quién me ha enviado un mensaje para decirme que está jugando y quién no. Normalmente se necesitan 10 personas para un juego, 8 a la vez. Hay una lista de unas 20 personas que pueden o no poder jugar cada juego.

La idea de la aplicación en la que me decidí era algo que permitía elegir jugadores de una lista, dándome un recuento de cuántos jugadores habían confirmado que podían jugar.

A medida que lo pensaba más, sentí que podía ampliar el alcance un poco más para que pudiera usarse para organizar cualquier actividad simple basada en equipos.

Es cierto que apenas había soñado con Google Earth. Sin embargo, tenía todos los desafíos esenciales: diseño, gestión de datos, interactividad, almacenamiento de datos, organización del código.

En cuanto al diseño, no me preocuparía por otra cosa que no fuera una versión que pudiera ejecutarse y funcionar bien en la ventana gráfica de un teléfono. Limitaría los desafíos de diseño a resolver los problemas solo en pantallas pequeñas.

La idea central ciertamente se inclinó hacia las aplicaciones de estilo 'tareas pendientes', de las cuales había montones de ejemplos existentes para buscar inspiración y, al mismo tiempo, tenían la diferencia suficiente para proporcionar algunos desafíos únicos de diseño y codificación.

Características previstas

Una lista inicial de viñetas de características que tenía la intención de diseñar y codificar se veía así:

  • Un cuadro de entrada para agregar personas a la lista;
  • La capacidad de configurar a cada persona para 'adentro' o 'afuera';
  • Una herramienta que divide a las personas en equipos, por defecto en 2 equipos;
  • La capacidad de eliminar a una persona de la lista;
  • Alguna interfaz para 'herramientas'. Además de dividir, las herramientas disponibles deben incluir la capacidad de descargar los datos ingresados ​​como un archivo, cargar datos guardados previamente y eliminar todos los jugadores de una sola vez;
  • La aplicación debe mostrar un recuento actual de cuántas personas están 'Entradas';
  • Si no hay personas seleccionadas para un juego, debería ocultar el separador de equipos;
  • Modo de pago. Un interruptor en la configuración que permite a los usuarios "internos" tener un interruptor adicional para mostrar si han pagado o no.

Al principio, esto es lo que consideré las características de un producto mínimo viable.

Diseño

Los diseños comenzaron en trozos de papel. Fue esclarecedor (léase: aplastante) descubrir cuántas ideas que eran increíbles en mi cabeza resultaron ser ridículas cuando se sometieron incluso al exiguo escrutinio que ofrece un dibujo a lápiz.

Por lo tanto, muchas ideas se descartaron rápidamente, pero la otra cara de la moneda fue que al esbozar algunas ideas, invariablemente condujo a otras ideas que de otro modo nunca habría considerado.

Ahora, los diseñadores que lean esto probablemente dirán: "Obviamente, por supuesto", pero esto fue una verdadera revelación para mí. Los desarrolladores están acostumbrados a ver diseños de etapas posteriores, y rara vez ven todos los pasos abandonados en el camino antes de ese punto.

Una vez satisfecho con algo como un dibujo a lápiz, intentaría recrearlo en el paquete de diseño, Sketch. Así como las ideas desaparecieron en la etapa de papel y lápiz, un número igual no logró pasar a la siguiente etapa de fidelidad de Sketch. Los que parecían resistir como mesas de trabajo en Sketch se eligieron como candidatos para codificar.

A su vez, encontraría que cuando esos candidatos eran código integrado, un porcentaje tampoco funcionó por diversas razones. Cada paso de fidelidad expuso nuevos desafíos para que el diseño pasara o fallara. Y un fracaso me llevaría literal y figurativamente de vuelta al tablero de dibujo.

Como tal, en última instancia, el diseño con el que terminé es bastante diferente al que tenía originalmente en Sketch. Aquí están las primeras maquetas de Sketch:

Diseño inicial de la aplicación Who's In
Diseño inicial de la aplicación Who's In (vista previa grande)
Menú inicial de la aplicación Who's In
Menú inicial de la aplicación Who's In (vista previa grande)

Incluso entonces, no me engañaba; era un diseño básico. Sin embargo, en este punto tenía algo que estaba relativamente seguro de que podía funcionar y estaba ansioso por intentar construirlo.

Requerimientos técnicos

Con algunos requisitos de características iniciales y una dirección visual básica, era hora de considerar qué se debería lograr con el código.

Aunque la sabiduría recibida dicta que la forma de hacer aplicaciones para dispositivos iOS o Android es con código nativo, ya establecimos que mi intención era construir la aplicación con JavaScript.

También quería asegurarme de que la aplicación cumplía todos los requisitos necesarios para calificar como una aplicación web progresiva, o PWA, como se les conoce más comúnmente.

En caso de que no sepa qué es una aplicación web progresiva, aquí está el 'discurso de ascensor'. Conceptualmente, solo imagine una aplicación web estándar pero que cumpla con algunos criterios particulares. El cumplimiento de este conjunto de requisitos particulares significa que un dispositivo compatible (piense en un teléfono móvil) otorga privilegios especiales a la aplicación web, lo que hace que la aplicación web sea más grande que la suma de sus partes.

En Android, en particular, puede ser casi imposible distinguir una PWA, creada solo con HTML, CSS y JavaScript, de una aplicación creada con código nativo.

Aquí está la lista de requisitos de Google para que una aplicación se considere una aplicación web progresiva:

  • El sitio se sirve a través de HTTPS;
  • Las páginas responden en tabletas y dispositivos móviles;
  • Todas las URL de la aplicación se cargan sin conexión;
  • Metadatos proporcionados para Agregar a la pantalla de inicio;
  • Primera carga rápida incluso en 3G;
  • El sitio funciona entre navegadores;
  • Las transiciones de página no parecen bloquearse en la red;
  • Cada página tiene una URL.

Ahora, además, si realmente quiere ser la mascota del maestro y que su aplicación sea considerada como una 'Aplicación web progresiva ejemplar', entonces también debe cumplir con los siguientes requisitos:

  • El contenido del sitio está indexado por Google;
  • Los metadatos de Schema.org se proporcionan cuando corresponde;
  • Los metadatos sociales se proporcionan cuando corresponde;
  • Las URL canónicas se proporcionan cuando es necesario;
  • Las páginas usan la API de historial;
  • El contenido no salta cuando se carga la página;
  • Presionar hacia atrás desde una página de detalles retiene la posición de desplazamiento en la página de lista anterior;
  • Cuando se toca, las entradas no quedan ocultas por el teclado en pantalla;
  • El contenido se puede compartir fácilmente desde el modo independiente o de pantalla completa;
  • El sitio responde a todos los tamaños de pantalla de teléfonos, tabletas y computadoras de escritorio;
  • Las indicaciones de instalación de la aplicación no se usan en exceso;
  • Se intercepta el indicador Agregar a la pantalla de inicio;
  • Primera carga muy rápida incluso en 3G;
  • El sitio utiliza redes de caché primero;
  • El sitio informa adecuadamente al usuario cuando está desconectado;
  • Proporcionar contexto al usuario sobre cómo se utilizarán las notificaciones;
  • La interfaz de usuario que alienta a los usuarios a activar las notificaciones automáticas no debe ser demasiado agresiva;
  • El sitio atenúa la pantalla cuando se muestra la solicitud de permiso;
  • Las notificaciones automáticas deben ser oportunas, precisas y relevantes;
  • Proporciona controles para habilitar y deshabilitar notificaciones;
  • El usuario inicia sesión en todos los dispositivos a través de la API de administración de credenciales;
  • El usuario puede pagar fácilmente a través de la interfaz de usuario nativa desde la API de solicitud de pago.

Crikey! ¡No sé ustedes, pero ese segundo grupo de cosas parece mucho trabajo para una aplicación básica! Da la casualidad de que hay muchos elementos allí que no son relevantes para lo que había planeado de todos modos. A pesar de eso, no me avergüenza decir que bajé la vista para pasar solo las pruebas iniciales.

Para toda una sección de tipos de aplicaciones, creo que una PWA es una solución más aplicable que una aplicación nativa. Donde podría decirse que los juegos y SaaS tienen más sentido en una tienda de aplicaciones, las utilidades más pequeñas pueden vivir felizmente y con más éxito en la web como aplicaciones web progresivas.

Mientras que en el tema de eludir el trabajo duro, otra opción que se tomó al principio fue tratar de almacenar todos los datos de la aplicación en el propio dispositivo de los usuarios. De esa manera, no sería necesario conectarse con servicios y servidores de datos y lidiar con inicios de sesión y autenticaciones. ¡Por donde estaban mis habilidades, descifrar la autenticación y almacenar datos de usuario parecía casi seguro que sería más de lo que podía masticar y exagerar para el mandato de la aplicación!

Opciones tecnológicas

Con una idea bastante clara de cuál era el objetivo, la atención se centró en las herramientas que podrían emplearse para construirlo.

Al principio decidí usar TypeScript, que se describe en su sitio web como "... un superconjunto escrito de JavaScript que se compila en JavaScript simple". Lo que había visto y leído del lenguaje me gustaba, especialmente el hecho de que aprendiera tan bien el análisis estático.

El análisis estático simplemente significa que un programa puede mirar su código antes de ejecutarlo (por ejemplo, cuando es estático) y resaltar problemas. No necesariamente puede señalar problemas lógicos, pero puede señalar código no conforme con un conjunto de reglas.

Cualquier cosa que pudiera señalar mis (seguro que habrá muchos) errores a medida que avanzaba tenía que ser algo bueno, ¿verdad?

Si no está familiarizado con TypeScript, considere el siguiente código en JavaScript estándar:

 console.log(`${count} players`); let count = 0;

Ejecute este código y obtendrá un error similar a:

 ReferenceError: Cannot access uninitialized variable.

Para aquellos con un poco de destreza en JavaScript, para este ejemplo básico, no necesitan una herramienta que les diga que las cosas no terminarán bien.

Sin embargo, si escribe ese mismo código en TypeScript, esto sucede en el editor:

Mecanografiado en acción
TypeScript en acción (vista previa grande)

¡Estoy recibiendo algunos comentarios sobre mi idiotez incluso antes de ejecutar el código! Esa es la belleza del análisis estático. Esta retroalimentación a menudo era como tener un desarrollador más experimentado sentado conmigo detectando errores a medida que avanzaba.

TypeScript principalmente, como su nombre lo indica, le permite especificar el 'tipo' esperado para cada cosa en el código. Esto evita que inadvertidamente 'coaccione' un tipo a otro. O intentar ejecutar un método en un dato que no es aplicable, por ejemplo, un método de matriz en un objeto. Este no es el tipo de cosa que necesariamente resulta en un error cuando se ejecuta el código, pero ciertamente puede introducir errores difíciles de rastrear. Gracias a TypeScript, obtiene comentarios en el editor incluso antes de intentar ejecutar el código.

Ciertamente, TypeScript no fue esencial en este viaje de descubrimiento y nunca alentaría a nadie a usar herramientas de esta naturaleza a menos que haya un beneficio claro. Configurar herramientas y configurarlas en primer lugar puede ser una pérdida de tiempo, así que definitivamente considere su aplicabilidad antes de sumergirse.

TypeScript ofrece otros beneficios que veremos en el próximo artículo de esta serie, pero las capacidades de análisis estático fueron suficientes para que yo quisiera adoptar TypeScript.

Hubo consideraciones en cadena de las elecciones que estaba haciendo. Optar por construir la aplicación como una aplicación web progresiva significaba que tendría que entender a los trabajadores de servicios hasta cierto punto. Usar TypeScript significaría introducir herramientas de construcción de algún tipo. ¿Cómo manejaría esas herramientas? Históricamente, había usado NPM como administrador de paquetes, pero ¿qué pasa con Yarn? ¿Valió la pena usar Yarn en su lugar? Centrarse en el rendimiento significaría considerar algunas herramientas de minificación o agrupación; herramientas como webpack se estaban volviendo cada vez más populares y necesitarían ser evaluadas.

Resumen

Reconocí la necesidad de embarcarme en esta búsqueda. Mis poderes de JavaScript eran débiles y nada ciñe los lomos tanto como intentar poner la teoría en práctica. Decidir construir una aplicación web con JavaScript estándar iba a ser mi bautismo de fuego.

Pasé algún tiempo investigando y considerando las opciones para hacer la aplicación y decidí que convertirla en una aplicación web progresiva tenía más sentido para mi conjunto de habilidades y la relativa simplicidad de la idea.

Necesitaría herramientas de compilación, un administrador de paquetes y, posteriormente, mucha paciencia.

En última instancia, en este punto quedaba la pregunta fundamental: ¿era esto algo que realmente podía manejar? ¿O me sentiría humillado por mi propia ineptitud?

Espero que me acompañen en la segunda parte cuando puedan leer sobre herramientas de compilación, patrones de diseño de JavaScript y cómo hacer algo más 'similar a una aplicación'.