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

应用网域

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

HIIT 训练在线课程,带有绿色的高强度计时器。
活跃期。
在线 HIIT 训练课程,显示红色低强度定时器。
休息时段。

HIIT 时间示例应用

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

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

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

HIIT Time 示例应用(纵向模式)。
竖屏模式下的 HIIT 时间“健身”标签页。
横屏模式下的 HIIT 时间示例应用。
横屏模式下的 HIIT 时间“锻炼”标签页。
HIIT Time 示例应用,显示了定时器的管理。
HIIT 时间定时器管理。

应用结构

如上所述,该应用由一个侧边导航栏、一个标签页栏和三个页面组成,这些页面以网格形式排列。导航栏和标签栏以 iframe 的形式实现,中间有一个 <div> 容器,另外还有三个用于网页的 iframe,其中一个始终可见,并且取决于标签栏中的有效选择。系统会针对动态创建的应用内页面投放指向 about:blank 的最终 iframe,从而修改现有计时器或创建新计时器。我将这种模式称为多页单页应用 (MPSPA)。

Chrome DevTools 中显示的应用 HTML 结构视图,其中显示该结构由 6 个 iframe 组成:一个用于导航栏,一个用于标签页栏,三个用于应用的每个页面,最后一个占位符 iframe 用于动态页面。
该应用由 6 个 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 之间进行导航。

样式

页面的样式设置是在每个页面自己的 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 嵌入中试用(该 iframe 会模拟移动设备)。

致谢

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