Die App-Domain
Um die Programmierung von Mini-Apps auf eine Web-App anzuwenden, brauchte ich eine kleine, aber vollständige App-Idee. Hochintensives Intervalltraining (HIIT) ist eine Strategie für Herz-Kreislauf-Übungen, bei der sich kurze Phasen intensiver anaerober Übungen mit weniger intensiven Erholungsphasen abwechseln. Viele HIIT-Trainings verwenden HIIT-Timer, z. B. diese 30-minütige Online-Sitzung vom YouTube-Kanal The Body Coach TV.
Beispiel-App „HIIT Time“
Für dieses Kapitel habe ich ein einfaches Beispiel für eine solche HIIT-Timer-Anwendung mit dem passenden Namen „HIIT Time“ erstellt. Damit kann der Nutzer verschiedene Timer definieren und verwalten, die immer aus einem Intervall mit hoher und einem mit niedriger Intensität bestehen. Anschließend kann er einen davon für eine Trainingseinheit auswählen. Es handelt sich um eine responsive App mit einer Navigationsleiste, einer Tabbar und drei Seiten:
- Workout:Die aktive Seite während eines Workouts. Hier kann der Nutzer einen der Timer auswählen. Außerdem gibt es drei Fortschrittsringe: die Anzahl der Sätze, die aktive Phase und die Ruhephase.
- Timers:Hier werden vorhandene Timer verwaltet und neue erstellt.
- Preferences:Hier können Soundeffekte und Sprachausgabe aktiviert und deaktiviert sowie Sprache und Design ausgewählt werden.
Die folgenden Screenshots geben einen Eindruck von der Anwendung.
App-Struktur
Wie oben beschrieben, besteht die App aus einer Navigationsleiste, einer Tabbar und drei Seiten, die in einem Raster angeordnet sind.
Navigationsleiste und Tabbar werden als iFrames mit einem <div> Container dazwischen realisiert. Außerdem gibt es drei weitere iFrames
für die Seiten, von denen immer einer sichtbar ist und von der aktiven Auswahl in der Tabbar abhängt.
Ein letzter iFrame, der auf about:blank verweist, dient für dynamisch erstellte In-App-Seiten, die zum Ändern vorhandener oder zum Erstellen neuer Timer erforderlich sind.
Ich nenne dieses Muster „Multi-Page Single-Page App“ (MPSPA).
Komponentenbasiertes lit-html-Markup
Die Struktur jeder Seite wird als lit-html-Gerüst
, das zur Laufzeit dynamisch ausgewertet wird, realisiert.
Lit-html ist eine effiziente, ausdrucksstarke und erweiterbare HTML-Templating-Bibliothek für JavaScript.
Wenn Sie sie direkt in den HTML-Dateien verwenden, ist das mentale Programmiermodell direkt ausgabeorientiert.
Als Programmierer schreiben Sie eine Vorlage für die endgültige Ausgabe. Lit-html füllt dann die Lücken dynamisch anhand Ihrer Daten und verknüpft die Event-Listener.
Die App verwendet benutzerdefinierte Elemente von Drittanbietern wie Shoelace's <sl-progress-ring> oder ein selbst implementiertes benutzerdefiniertes Element namens <human-duration>.
Da benutzerdefinierte Elemente eine deklarative API haben (z. B. das Attribut percentage des Fortschrittsrings), funktionieren sie gut mit lit-html, wie Sie in der folgenden Auflistung sehen können.
<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>
Programmiermodell
Jede Seite hat eine entsprechende Page-Klasse, die das lit-html-Markup mit Leben füllt, indem sie Implementierungen der Event-Handler und die Daten für jede Seite bereitstellt.
Diese Klasse unterstützt auch Lebenszyklusmethoden wie onShow(), onHide(), onLoad() und onUnload().
Seiten haben Zugriff auf einen Datenspeicher, der zum Freigeben von optional persistentem seitenbezogenem und globalem Status dient.
Alle Strings werden zentral verwaltet, sodass die Internationalisierung integriert ist.
Das Routing wird im Wesentlichen kostenlos vom Browser übernommen, da die App nur die Sichtbarkeit von iFrames umschaltet und für dynamisch erstellte Seiten das Attribut src des Platzhalter-iFrames ändert.
Das folgende Beispiel zeigt den Code zum Schließen einer dynamisch erstellten Seite.
import Page from '../page.js';
const page = new Page({
eventHandlers: {
back: (e) => {
e.preventDefault();
window.top.history.back();
},
},
});
Stile
Die Stile der Seiten werden seitenweise in einer eigenen CSS-Datei mit Bereich festgelegt.
Das bedeutet, dass Elemente in der Regel direkt über ihre Elementnamen angesprochen werden können, da keine Konflikte mit anderen Seiten auftreten können.
Globale Stile werden jeder Seite hinzugefügt, sodass zentrale Einstellungen wie font-family oder box-sizing nicht wiederholt deklariert werden müssen.
Hier werden auch die Designs und Optionen für den dunklen Modus definiert.
Die folgende Auflistung zeigt die Regeln für die Seite „Preferences“, auf der die verschiedenen Formularelemente in einem Raster angeordnet sind.
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;
}
Bildschirm-Wakelock
Während eines Workouts sollte sich der Bildschirm nicht ausschalten. In Browsern, die dies unterstützen, wird dies in HIIT Time durch einen Bildschirm-Wakelock realisiert. Das folgende Snippet zeigt, wie das geht.
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();
}
});
}
Anwendung testen
Die HIIT Time-Anwendung ist auf GitHub verfügbar. Sie können die Demo in einem neuen Fenster oder direkt im unten eingebetteten iFrame ausprobieren, das ein Mobilgerät simuliert.
Danksagungen
Dieser Artikel wurde von Joe Medley, Kayce Basques, Milica Mihajlija, Alan Kent, und Keith Gu überprüft.