在示例项目中运用迷你应用编程原则

应用网域

为了展示应用于 Web 应用的小程序编程方式,我需要一个足够完整的小应用创意。高强度间歇训练 (HIIT) 是一种心血管锻炼策略,即交替进行短时间的高强度无氧运动和低强度恢复期。 许多 HIIT 训练都使用 HIIT 计时器,例如 The Body Coach TV YouTube 频道提供的这个时长 30 分钟的在线课程

HIIT 训练在线课程,带有绿色高强度计时器。
有效期限。
HIIT 训练在线课程,红色低强度计时器。
休息时间。

HIIT Time 示例应用

在本章中,我构建了一个名为“HIIT Time”的此类 HIIT 计时器应用的基本示例,该应用可让用户定义和管理各种计时器(始终包含高强度和低强度间隔),然后选择其中一个计时器进行训练。这是一个自适应应用,包含导航栏、标签栏和三个页面:

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

以下屏幕截图展示了该应用。

竖屏模式下的 HIIT Time 示例应用。
竖屏模式下的 HIIT Time“锻炼”标签页。
横屏模式下的 HIIT Time 示例应用。
横屏模式下的 HIIT Time“锻炼”标签页。
显示计时器管理的 HIIT Time 示例应用。
HIIT Time 计时器管理。

应用结构

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

Chrome 开发者工具视图,显示了应用的 HTML 结构,其中包含六个 iframe:一个用于导航栏,一个用于标签栏,三个分组的 iframe 分别用于应用的每个页面,最后一个占位 iframe 用于动态页面。
该应用包含六个 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>
三个按钮和一个进度环。
与上述标记对应的网页呈现部分。

编程模型

每个页面都有一个对应的 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();
    },
  },
});
以 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 应用偏好设置页面,其中显示了网格布局的表单。
每个页面都是一个独立的世界。样式设置直接通过元素名称进行。

屏幕唤醒锁定

在锻炼期间,屏幕不应关闭。在支持此功能的浏览器中,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。