Stosowanie zasad programowania miniaplikacji do przykładowego projektu

Domena aplikacji

Aby pokazać, jak programowanie miniaplikacji można zastosować w aplikacji internetowej, potrzebowałem małego, ale wystarczająco kompletnego pomysłu na aplikację. Trening interwałowy o wysokiej intensywności (HIIT) to strategia ćwiczeń sercowo-naczyniowych polegająca na naprzemiennych seriach krótkich okresów intensywnych ćwiczeń beztlenowych z mniej intensywnymi okresami regeneracji. Wiele treningów HIIT korzysta z timerów HIIT, np. ta 30-minutowa sesja online z kanału The Body Coach TV na YouTube.

Sesja treningu HIIT online z zielonym timerem o wysokiej intensywności.
Okres aktywności.
Sesja treningu HIIT online z czerwonym stoperem o niskiej intensywności.
Okres odpoczynku.

Przykładowa aplikacja HIIT Time

Na potrzeby tego rozdziału stworzyłem podstawowy przykład takiej aplikacji z timerem HIIT o nazwie „HIIT Time”, która umożliwia użytkownikowi definiowanie i zarządzanie różnymi timerami, zawsze składającymi się z interwału o wysokiej i niskiej intensywności, a następnie wybranie jednego z nich na sesję treningową. Jest to aplikacja responsywna z paskiem nawigacyjnym, paskiem kart i 3 stronami:

  • Trening: aktywna strona podczas treningu. Umożliwia użytkownikowi wybranie jednego z timerów i zawiera 3 pierścienie postępu: liczbę serii, okres aktywności i okres odpoczynku.
  • Timery: zarządza istniejącymi timerami i umożliwia użytkownikowi tworzenie nowych.
  • Ustawienia: umożliwia włączanie i wyłączanie efektów dźwiękowych i odpowiedzi głosowej, a także wybieranie języka i motywu.

Poniższe zrzuty ekranu dają wyobrażenie o aplikacji.

Przykładowa aplikacja HIIT Time w trybie pionowym.
Karta „Trening” w aplikacji HIIT Time w trybie pionowym.
Przykładowa aplikacja HIIT Time w trybie poziomym.
Karta „Trening” w aplikacji HIIT Time w trybie poziomym.
Przykładowa aplikacja HIIT Time pokazująca zarządzanie timerem.
Zarządzanie timerami w aplikacji HIIT Time.

Struktura aplikacji

Jak wspomnieliśmy powyżej, aplikacja składa się z paska nawigacyjnego, paska kart i 3 stron ułożonych w siatce. Pasek nawigacyjny i pasek kart są realizowane jako elementy iframe z kontenerem <div> między nimi i 3 kolejnymi elementami iframe na strony. Jeden z nich jest zawsze widoczny i zależy od aktywnego wyboru na pasku kart. Ostatni element iframe wskazujący about:blank służy do dynamicznego tworzenia stron w aplikacji, które są potrzebne do modyfikowania istniejących timerów lub tworzenia nowych. Ten wzorzec nazywam aplikacją wielostronicową (MPSPA).

Widok Narzędzi deweloperskich w Chrome przedstawiający strukturę HTML aplikacji. Składa się ona z 6 elementów iframe: 1 dla paska nawigacyjnego, 1 dla paska kart i 3 zgrupowanych dla każdej strony aplikacji, a także 1 elementu iframe będącego symbolem zastępczym dla stron dynamicznych.
Aplikacja składa się z 6 elementów iframe.

Znaczniki lit-html oparte na komponentach

Struktura każdej strony jest realizowana jako szkielet lit-html który jest dynamicznie oceniany w czasie działania. Lit-html to wydajna, ekspresywna i rozszerzalna biblioteka szablonów HTML dla JavaScriptu. Dzięki używaniu jej bezpośrednio w plikach HTML model programowania mentalnego jest bezpośrednio zorientowany na dane wyjściowe. Jako programista piszesz szablon tego, jak będą wyglądać dane wyjściowe, a lit-html dynamicznie wypełnia luki na podstawie Twoich danych i podłącza detektory zdarzeń. Aplikacja korzysta z elementów niestandardowych innych firm, takich jak Shoelace's <sl-progress-ring> lub z samodzielnie zaimplementowanego elementu niestandardowego o nazwie <human-duration>. Ponieważ elementy niestandardowe mają deklaratywny interfejs API (np. atrybut percentage okręgu postępu), dobrze współpracują z lit-html, co widać w poniższym przykładzie.

<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>
3 przyciski i okrąg postępu.
Wyrenderowana sekcja strony odpowiadająca powyższym znacznikom.

Model programowania

Każda strona ma odpowiadającą jej klasę Page, która wypełnia znaczniki lit-html, udostępniając implementacje obsługi zdarzeń i dane dla każdej strony. Ta klasa obsługuje też metody cyklu życia, takie jak onShow(), onHide(), onLoad() i onUnload(). Strony mają dostęp do magazynu danych, który służy do udostępniania opcjonalnie utrwalonego stanu na stronie i stanu globalnego. Wszystkie ciągi tekstowe są zarządzane centralnie, więc internacjonalizacja jest wbudowana. Routing jest obsługiwany przez przeglądarkę zasadniczo bezpłatnie, ponieważ aplikacja tylko przełącza widoczność elementu iframe i w przypadku stron utworzonych dynamicznie zmienia atrybut src elementu iframe zastępczego. Poniższy przykład pokazuje kod zamykania strony utworzonej dynamicznie.

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

const page = new Page({
  eventHandlers: {
    back: (e) => {
      e.preventDefault();
      window.top.history.back();
    },
  },
});
Strona w aplikacji zrealizowana jako element iframe.
Nawigacja odbywa się między elementami iframe.

Styl

Stylizacja stron odbywa się na każdej stronie w jej własnym pliku CSS o ograniczonym zakresie. Oznacza to, że elementy można zwykle adresować bezpośrednio za pomocą ich nazw, ponieważ nie mogą wystąpić konflikty z innymi stronami. Style globalne są dodawane do każdej strony, więc ustawienia centralne, takie jak font-family czy box-sizing, nie muszą być deklarowane wielokrotnie. W tym miejscu definiowane są też motywy i opcje trybu ciemnego. Poniższy przykład pokazuje reguły strony Ustawienia, które układają różne elementy formularza w siatce.

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;
}
Strona ustawień aplikacji HIIT Time z formularzem w układzie siatki.
Każda strona to osobny świat. Stylizacja odbywa się bezpośrednio za pomocą nazw elementów.

Blokada uśpienia ekranu

Podczas treningu ekran nie powinien się wyłączać. W przeglądarkach, które to obsługują, aplikacja HIIT Time realizuje to za pomocą blokady wybudzania ekranu. Poniższy fragment kodu pokazuje, jak to zrobić.

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();
    }
  });
}

Testowanie aplikacji

Aplikacja HIIT Time jest dostępna na GitHub. Możesz wypróbować wersję demonstracyjną w nowym oknie, lub bezpośrednio w elemencie iframe poniżej, który symuluje urządzenie mobilne.

Podziękowania

Ten artykuł został sprawdzony przez Joe Medleya, Kayce Basques, Milicę Mihajliję, Alana Kenta, i Keitha Gu.