Aplicar los principios de programación de miniapps a un proyecto de ejemplo

El dominio de la app

Para mostrar la forma de programación de mini app aplicada a una app web, necesitaba una idea de app pequeña pero lo suficientemente completa. El entrenamiento de intervalos de alta intensidad (HIIT) es una estrategia de ejercicio cardiovascular que alterna series de períodos cortos de ejercicio anaeróbico intenso con períodos de recuperación menos intensos. Muchos entrenamientos HIIT usan temporizadores HIIT, por ejemplo, esta sesión en línea de 30 minutos del canal de YouTube The Body Coach TV.

Sesión de entrenamiento HIIT en línea con un temporizador verde de alta intensidad.
Es el período activo.
Sesión de entrenamiento HIIT en línea con un temporizador rojo de baja intensidad.
Período de descanso.

App de ejemplo de HIIT Time

Para este capítulo, creé un ejemplo básico de una aplicación de temporizador HIIT llamada "HIIT Time" que permite al usuario definir y administrar varios temporizadores, que siempre constan de un intervalo de alta y otro de baja intensidad, y, luego, seleccionar uno de ellos para una sesión de entrenamiento. Es una app responsiva con una barra de navegación, una barra de pestañas y tres páginas:

  • Entrenamiento: Es la página activa durante un entrenamiento. Permite al usuario seleccionar uno de los temporizadores y cuenta con tres anillos de progreso: la cantidad de series, el período activo y el período de descanso.
  • Temporizadores: Administra los temporizadores existentes y permite que el usuario cree otros nuevos.
  • Preferencias: Permite activar o desactivar los efectos de sonido y la respuesta hablada, y seleccionar el idioma y el tema.

Las siguientes capturas de pantalla muestran una impresión de la aplicación.

App de ejemplo de HIIT Time en modo vertical.
Pestaña HIIT Time "Workout" en modo vertical.
App de ejemplo de HIIT Time en modo horizontal.
Pestaña HIIT Time "Entrenamiento" en modo horizontal.
App de ejemplo de HIIT Time que muestra la administración de un temporizador.
Administración del temporizador de HIIT Time.

Estructura de app

Como se describió anteriormente, la app consta de una barra de navegación, una barra de pestañas y tres páginas, dispuestas en una cuadrícula. La barra de navegación y la barra de pestañas se implementan como iframes con un contenedor <div> entre ellos con tres iframes más para las páginas, de los cuales uno siempre está visible y depende de la selección activa en la barra de pestañas. Un iframe final que apunta a about:blank sirve para las páginas integradas en la app creadas de forma dinámica, que son necesarias para modificar los temporizadores existentes o crear otros nuevos. A este patrón lo llamo aplicación de página única de varias páginas (MPSPA).

Vista de Herramientas para desarrolladores de Chrome de la estructura HTML de la app, que muestra que consta de seis elementos iframe: uno para la barra de navegación, uno para la barra de pestañas y tres agrupados para cada página de la app, con un iframe de marcador de posición final para las páginas dinámicas.
La app consta de seis iframes.

Lenguaje de marcado lit-html basado en componentes

La estructura de cada página se realiza como un andamio lit-html que se evalúa de forma dinámica en el tiempo de ejecución. Para obtener información general sobre lit-html, se trata de una biblioteca de plantillas HTML eficiente, expresiva y extensible para JavaScript. Al usarlo directamente en los archivos HTML, el modelo de programación mental se orienta directamente a la salida. Como programador, escribes una plantilla de cómo se verá el resultado final, y luego lit-html completa los espacios de forma dinámica según tus datos y conecta los objetos de escucha de eventos. La app usa elementos personalizados de terceros, como <sl-progress-ring> de Shoelace o un elemento personalizado implementado por el usuario llamado <human-duration>. Dado que los elementos personalizados tienen una API declarativa (por ejemplo, el atributo percentage del anillo de progreso), funcionan bien con lit-html, como puedes ver en el siguiente listado.

<div>
  <button class="start" @click="${eventHandlers.start}" type="button">
    ${strings.START}
  </button>
  <button class="pause" @click="${eventHandlers.pause}" type="button">
    ${strings.PAUSE}
  </button>
  <button class="reset" @click="${eventHandlers.reset}" type="button">
    ${strings.RESET}
  </button>
</div>

<div class="progress-rings">
  <sl-progress-ring
    class="sets"
    percentage="${Math.floor(data.sets/data.activeTimer.sets*100)}"
  >
    <div class="progress-ring-caption">
      <span>${strings.SETS}</span>
      <span>${data.sets}</span>
    </div>
  </sl-progress-ring>
</div>
Tres botones y un círculo de progreso.
Es la sección renderizada de la página que corresponde al marcado anterior.

Modelo de programación

Cada página tiene una clase Page correspondiente que llena el lenguaje de marcado de lit-html con vida, ya que proporciona implementaciones de los controladores de eventos y los datos de cada página. Esta clase también admite métodos de ciclo de vida, como onShow(), onHide(), onLoad() y onUnload(). Las páginas tienen acceso a un almacén de datos que sirve para compartir el estado global y el estado por página que se persiste de forma opcional. Todas las cadenas se administran de forma centralizada, por lo que la internacionalización está integrada. El navegador controla el enrutamiento de forma casi gratuita, ya que todo lo que hace la app es alternar la visibilidad del iframe y, para las páginas creadas de forma dinámica, cambiar el atributo src del iframe de marcador de posición. En el siguiente ejemplo, se muestra el código para cerrar una página creada de forma dinámica.

import Page from '../page.js';

const page = new Page({
  eventHandlers: {
    back: (e) => {
      e.preventDefault();
      window.top.history.back();
    },
  },
});
Página integrada en la app que se muestra como un iframe.
La navegación se realiza de iframe a iframe.

Diseño

El diseño de las páginas se realiza por página en su propio archivo CSS con alcance. Esto significa que, por lo general, los elementos se pueden direccionar directamente por sus nombres, ya que no pueden producirse conflictos con otras páginas. Los estilos globales se agregan a cada página, por lo que no es necesario declarar de forma reiterada la configuración central, como font-family o box-sizing. Aquí también se definen los temas y las opciones del modo oscuro. En la siguiente lista, se muestran las reglas de la página Preferencias que disponen los distintos elementos del formulario en una cuadrícula.

main {
  max-width: 600px;
}

form {
  display: grid;
  grid-template-columns: auto 1fr;
  grid-gap: 0.5rem;
  margin-block-end: 1rem;
}

label {
  text-align: end;
  grid-column: 1 / 2;
}

input,
select {
  grid-column: 2 / 3;
}
Página de preferencias de la app de HIIT Time que muestra un formulario en diseño de cuadrícula.
Cada página es un mundo en sí mismo. El diseño se realiza directamente con los nombres de los elementos.

Bloqueo de activación de pantalla

Durante un entrenamiento, la pantalla no debe apagarse. En los navegadores que lo admiten, HIIT Time lo detecta a través de un bloqueo de activación de la pantalla. En el siguiente fragmento, se muestra cómo hacerlo.

if ('wakeLock' in navigator) {
  const requestWakeLock = async () => {
    try {
      page.shared.wakeLock = await navigator.wakeLock.request('screen');
      page.shared.wakeLock.addEventListener('release', () => {
        // Nothing.
      });
    } catch (err) {
      console.error(`${err.name}, ${err.message}`);
    }
  };
  // Request a screen wake lock…
  await requestWakeLock();
  // …and re-request it when the page becomes visible.
  document.addEventListener('visibilitychange', async () => {
    if (
      page.shared.wakeLock !== null &&
      document.visibilityState === 'visible'
    ) {
      await requestWakeLock();
    }
  });
}

Prueba la aplicación

La aplicación HIIT Time está disponible en GitHub. Puedes probar la demostración en una ventana nueva o directamente en el iframe incorporado a continuación, que simula un dispositivo móvil.

Agradecimientos

Joe Medley, Kayce Basques, Milica Mihajlija, Alan Kent y Keith Gu revisaron este artículo.