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

应用网域

为了展示适用于 Web 应用的迷你应用编程方式,我需要一个简短但足够完整的应用创意。高强度间歇训练 (HIIT) 是一种心血管运动策略,由两组短时间高强度无氧运动交替进行,同时训练强度较低。许多 HIIT 训练都使用 HIIT 计时器,例如来自 The Body Coach TV YouTube 频道的这个 30 分钟线上课程

线上 HIIT 训练课程,带有绿色高强度计时器。
活跃时间段。
线上 HIIT 训练课程,显示红色低强度计时器。
静息期。

HIIT 时间示例应用

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

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

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

竖屏模式下的 HIIT 时间示例应用。
竖屏模式下的 HIIT 时间“健身”标签页。
横屏模式下的 HIIT 时间示例应用。
横屏模式下的 HIIT 时间“健身”标签页。
展示计时器管理的 HIIT 时间示例应用。
HIIT 时间管理。

应用结构

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

Chrome 开发者工具视图:此应用的 HTML 结构,显示它由六个 iframe 组成:一个用于导航栏,一个用于标签页栏,而三个已分组的 iframe 用于应用的每个页面,而最终的占位 iframe 用于动态页面。
此应用包含 6 个 iframe。

基于组件的 lit-html 标记

每个页面的结构以 lit-html Scaffold 实现,该基架会在运行时动态评估。就 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 等中心设置。这里也是定义主题和深色模式选项的位置。下面列出了“Preferences”页面的规则,该页面在网格上布局了各种表单元素。

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 审阅。