将迷你应用程序编程原则应用于示例项目

应用领域

为了展示迷你应用程序的编程方式如何应用于 Web 应用程序,我需要一个小型但足够完整的应用程序想法。高强度间歇训练 (HIIT) 是一种心血管锻炼策略,它将短时间的剧烈无氧运动与不太剧烈的恢复期交替进行。许多 HIIT 训练使用 HIIT 计时器,例如,来自 The Body Coach TV YouTube 频道的这个 30 分钟在线课程

HIIT training online session with green high intensity timer.
活动期。
HIIT training online session with red low intensity timer.
休息期。

HIIT Time 示例应用

在本章中,我构建了一个名为“HIIT Time”的 HIIT 计时器应用程序的基本示例,该应用程序允许用户定义和管理各种计时器,这些计时器始终由高强度和低强度间隔组成,然后选择其中一个用于训练课程。它是一个响应式应用程序,带有导航栏、标签栏和三个页面

  • 锻炼: 锻炼期间的活动页面。它允许用户选择一个计时器,并具有三个进度环:组数、活动期和休息期。
  • 计时器: 管理现有计时器,并允许用户创建新的计时器。
  • 偏好设置: 允许切换声音效果和语音输出,以及选择语言和主题。

以下屏幕截图给出了应用程序的印象。

HIIT Time example app in portrait mode.
纵向模式下的 HIIT Time“锻炼”标签。
HIIT Time example app in landscape mode.
横向模式下的 HIIT Time“锻炼”标签。
HIIT Time example app showing management of a timer.
HIIT Time 计时器管理。

应用结构

如上所述,该应用由导航栏、标签栏和三个页面组成,排列在一个网格中。导航栏和标签栏被实现为 iframe,它们之间有一个 <div> 容器,其中包含三个用于页面的 iframe,其中一个始终可见,并且取决于标签栏中的活动选择。最后一个指向 about:blank 的 iframe 用于动态创建的应用内页面,这些页面是修改现有计时器或创建新计时器所必需的。我将此模式称为多页单页应用 (MPSPA)。

Chrome DevTools view of the HTML structure of the app showing that it consists of six iframes: one for the navbar, one for the tabbar, and three grouped ones for each page of the app, with a final placeholder iframe for dynamic pages.
该应用由六个 iframe 组成。

基于组件的 lit-html 标记

每个页面的结构都实现为 lit-html 脚手架,该脚手架在运行时动态评估。关于 lit-html 的背景知识,它是一个用于 JavaScript 的高效、富有表现力、可扩展的 HTML 模板库。通过直接在 HTML 文件中使用它,心智编程模型直接面向输出。作为程序员,您编写最终输出样式的模板,然后 lit-html 根据您的数据动态填充空白并连接事件侦听器。该应用程序使用了第三方自定义元素,例如 Shoelace<sl-progress-ring> 或一个名为 <human-duration> 的自行实现的自定义元素。由于自定义元素具有声明式 API(例如,进度环的 percentage 属性),因此它们与 lit-html 配合良好,如下面的列表所示。

<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>
Three buttons and a progress ring.
与上述标记对应的页面渲染部分。

编程模型

每个页面都有一个对应的 Page 类,该类通过提供事件处理程序的实现和提供每个页面的数据来为 lit-html 标记注入生命。此类还支持生命周期方法,例如 onShow()onHide()onLoad()onUnload()。页面可以访问数据存储,该数据存储用于共享可选的持久化按页面状态和全局状态。所有字符串都集中管理,因此内置了国际化。路由基本上由浏览器免费处理,因为应用程序所做的只是切换 iframe 可见性,并为动态创建的页面更改占位符 iframe 的 src 属性。下面的示例显示了关闭动态创建页面的代码。

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

const page = new Page({
  eventHandlers: {
    back: (e) => {
      e.preventDefault();
      window.top.history.back();
    },
  },
});
In-app page realized as an iframe.
导航发生在 iframe 到 iframe 之间。

样式

页面的样式设置在各自作用域内的 CSS 文件中按页面进行。这意味着通常可以直接通过元素名称来寻址元素,因为不会与其他页面发生冲突。全局样式被添加到每个页面,因此诸如 font-familybox-sizing 之类的中心设置无需重复声明。这也是定义主题和暗模式选项的地方。下面的列表显示了“偏好设置”页面的规则,该规则在网格上布局了各种表单元素。

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;
}
HIIT Time app preferences page showing a form in grid layout.
每个页面都是自己的世界。样式设置直接使用元素名称进行。

屏幕唤醒锁

在锻炼期间,屏幕不应关闭。在支持它的浏览器上,HIIT Time 通过屏幕唤醒锁来实现这一点。下面的代码片段展示了它是如何完成的。

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

测试应用程序

HIIT Time 应用程序可在 GitHub 上找到。您可以玩 演示,在新窗口中或直接在下面的 iframe 嵌入中,它模拟了移动设备。

致谢

本文由 Joe MedleyKayce BasquesMilica MihajlijaAlan Kent 和 Keith Gu 审阅。