AMP

在 AMP 页面中使用自定义 JavaScript

重要提示:本文档不适用于您当前选择的格式 故事

amp-script 允许您编写和运行自己的 JavaScript,同时保持 AMP 的性能保证。 大多数 AMP 组件通过它们自己的逻辑启用常见的 Web 交互,让您可以快速构建页面,而无需编写 JavaScript 或导入第三方库。 通过使用 amp-script,您可以针对特定用例或独特需求采用自定义逻辑,而不会失去 AMP 的优势。

本指南提供了有关此组件的背景知识及其使用最佳实践。

Web Worker

过多的 JavaScript 会使网站变慢且无响应。 为了控制 AMP 页面加载的 JavaScript 以及何时执行它,AMP 的验证规则禁止开发人员通过 <script> 标记在网页中运行 JavaScript。

Web Worker 提供了一种更安全地运行 JavaScript 的方法。 通常,所有 JavaScript 都在单个线程中运行,但每个 Worker 都在自己的线程中运行。 这是可能的,因为它们无法访问 DOM 或 window 对象,并且每个 Worker 都在自己的全局范围内运行。 因此,它们不会相互干扰工作或干扰主线程中代码引起的突变。 它们只能通过包含对象的消息与主线程以及彼此通信。 Worker 提供了一条通向多线程 Web 的路径,一种将 JavaScript 封装在沙箱中的方法,它不会阻塞 UI。

Worker 不会访问 DOM。 为了填补这一空白,AMP 团队创建了一个名为 WorkerDOM 的开源库。 WorkerDOM 将 DOM 复制到虚拟 DOM,并使该副本可供 Worker 使用。 WorkerDOM 还重新创建了标准 DOM API 的子集。 当 Worker 对虚拟 DOM 进行更改时,WorkerDOM 会在实际 DOM 中重新创建这些更改。 这使 Worker 可以使用标准技术操作 DOM 并在页面上进行更改。 DOM 同步仅在一个方向上进行。 如果主线程修改了 DOM,则不存在让 Worker 知道的机制。

amp-script 是 WorkerDOM 的包装器,它使 WorkerDOM 可在 AMP 中使用,提供与 AMP 功能的连接,建立开发人员 API,并引入保护用户体验的限制。 WorkerDOM 提供了 amp-script 功能的核心。

amp-script 概述

在 Worker 中,JavaScript 语言与浏览器中的其他位置相同。 因此,在 amp-script 中,您可以使用 JavaScript 提供的所有常用构造。 WorkerDOM 还重新创建了许多常用的 DOM API,并使其可供您使用。 它支持常见的 Web API,如 FetchCanvas,并为您提供了选定的全局对象,如 navigatorlocalStorage。 您可以像往常一样为浏览器事件分配处理程序。

但是,amp-script 不支持整个 DOM API 或 Web API,因为这将使其自身的 JavaScript 太大且笨重。 有关详细信息,请参阅文档,并参阅这些示例,了解 amp-script 的使用情况。

amp-script 用返回 Promise 的替代方法替换了一些同步 DOM API 方法。 例如,您可以使用 getBoundingClientRectAsync() 而不是 getBoundingClientRect()。 有时,对于提供对计算布局的同步访问的 DOM API 来说,这是必需的,有时是为了让 amp-script 可以调用本机浏览器方法并等待响应。

为了保持 AMP 的性能和布局稳定性保证,amp-script 带有一些限制。 如果 amp-script 容器的大小未固定,则只有在用户交互触发的情况下,您的代码才能进行更改。 您不能向 DOM 添加样式表或其他脚本,并且不支持 importScripts()。 有关详细信息,请参阅文档

使用 JavaScript 框架

由于 DOM API 未完全支持,如何在 amp-script 中最佳地处理 DOM 操作? 以下是两种可能性。

1) 了解支持的内容。 了解 支持的丰富 API 集。 您需要以不同的方式看待 DOM API - 不再是多年开发的大量属性和方法集,而更像是一组简洁的工具。

2) 使用 Preact。 React 不仅是一种流行的网站构建方式,而且它使用 DOM API 的子集来改变 DOM。 amp-script 可以完全支持这部分 API,因此完全支持 React。 也就是说,React 包通常会超过 amp-script150K 限制。 因此,建议您使用 Preact,它是 React 的轻量级替代方案。 Preact 旨在实现从 React 的直接迁移。 使用 Preact,您应该能够构建复杂的交互,而无需过多担心支持的内容。

该团队已经使用 VueAngularAurelialit-html 等框架测试了 amp-script,但测试程度较低。 如果您发现存在差距,请提交问题 - 或者,更好的是,提交拉取请求

由于 amp-script 无法详尽地支持 DOM API,因此简单地将 jQuery 等库复制到 <amp-script> 组件将不起作用。

用例

amp-script 扩展了 AMP 网页的功能范围。 由于它支持 DOM 和 Web API 的子集,并且由于其使用带有限制,因此它不是通用的 JavaScript 解决方案。 任何大量的现有 JavaScript 都可能需要修改才能在 amp-script 上下文中工作。

但是,amp-script 提供了一种很好的方法来处理现有 AMP 组件未提供的逻辑和交互。 以下是一些优秀的用例。

创建新的交互

amp-script 为您提供了 JavaScript 和 DOM API 的强大功能。 它允许您创建其他 AMP 组件无法实现的交互,从而打开通向 Web 完全创造力的大门。

也就是说,在转向 amp-script 创建新的交互之前,请检查是否可以使用 AMP 组件或组件组合来执行相同的操作。 利用现有的 AMP 组件最终会使您的代码更小、更易于维护。 如果某个组件可以实现与您想要的效果类似的效果,您或许可以使用 amp-script 自定义其行为

如果您使用 amp-script 创建其他开发人员可能感兴趣的交互,请考虑建议或贡献新的组件。

添加高级逻辑

当您的逻辑无法整齐地放入紧凑的表达式中时,amp-script 是最佳选择。 amp-bind 允许您将逻辑引入用户交互,但您的 JavaScript 需要放入单个表达式中。 由于代码封装在 HTML 属性中,因此它不会在您的 IDE 中获得语法突出显示的好处,您无法设置断点,并且调试可能是一个挑战。 在这种情况下,amp-script 允许您遵循最佳编程实践。

增强 AMP 组件

amp-script 可以访问 AMP 状态变量以及 AMP.getState()AMP.setState() 方法。 这提供了一条使用您自己的逻辑增强现有 AMP 组件的途径。 它还可以影响 <amp-script> 组件本身之外的 DOM。 有关示例,请参见 此处此处

替换 amp-bind 和 amp-list

如果您是 AMP 的新手,并且熟悉 JavaScript,那么可能会很想对每个浏览器交互都使用 amp-script。 但是,对于更简单的交互,您可能需要学习使用 amp-bind 和 AMP 的操作和事件系统。 对于更复杂的交互,或者需要更多逻辑和复杂状态变量操作的情况,amp-script 可能会更容易

处理服务器数据

AMP 允许您使用 amp-list 检索服务器数据,并使用 amp-mustache 进行格式化。在 mustache 模板不足以满足需求的情况下,amp-script 可以获取数据、进行格式化,并将格式化后的数据注入到 DOM 中。如果您需要在将服务器数据发送到 amp-mustache 之前对其进行处理,则 amp-script 函数可以作为 amp-list 的数据源。有关详细信息和代码示例,请参阅文档

引入新功能

您可以使用 amp-script 来利用 Web API 和 DOM API 中当前 AMP 组件无法访问的区域,或以 AMP 组件不支持的方式使用这些 API。例如,amp-script 支持 WebSockets示例)、localStorageCanvas。它支持各种浏览器事件,因此您可以监听超出 AMP 传递给传统组件的那些事件之外的事件。并且由于 amp-script 提供了对 navigator 对象的访问权限,您可以检索有关用户浏览器首选语言的信息。

何时替换 amp-bind 和 amp-list

对于熟悉 JavaScript 的新 AMP 开发人员来说,似乎总是使用 amp-script 比学习 amp-bindamp-list 更容易。但是,在某些情况下,amp-bindamp-list 更合适。

amp-bind 通常对于基本交互更加直接,它与 HTML 标签的紧密集成非常引人注目。在此示例中,按下按钮会更改文本片段。AMP 的数据绑定使此操作直接且易于阅读。

<p [text]="name">Rajesh</p>
<button on="tap:AMP.setState({name: 'Priya'})">I am Priya</button>

同样,当使用您控制其输出的 API 时,您可能能够在服务器上实现业务逻辑。您可以格式化 API 输出的数据,使其顺利地适应 amp-mustache 模板。在这些情况下,amp-list 非常适合。

amp-bind 还提供了一种直接的机制来在 AMP 组件之间进行通信。在此示例中,点击 <amp-selector> 中的图像会将状态变量 selectedSlide 设置为 0,这反过来会使 <amp-carousel> 移动到其第一个幻灯片。

<amp-carousel slide="selectedSlide">
...
</amp-carousel>

<amp-selector>
  <amp-img on="tap:AMP.setState({selectedSlide: 0})"/>
</amp-selector>

传统的交互式 AMP 组件也可能更适合跨越网页大部分区域的交互 - 因为您可能不希望将如此多的 DOM 包装在 <amp-script> 中。amp-listamp-bind 与 AMP 的其余部分紧密集成,使得在网页上的任何位置使用绑定都很方便。

但是,在涉及更复杂的状态变量或多个交互的页面上,使用 amp-script 将产生更简单且更易于维护的代码。以下示例来自AMP Camp 电子商务演示站点

<amp-selector
  name="color"
  layout="container"
  [selected]="product.selectedColor"
  on="select:AMP.setState({
      product: 
        {
          selectedSlide: product[event.targetOption].option - 1,
          selectedColor: event.targetOption,
          selectedSize: product[event.targetOption].sizes[product.selectedSize] != null ?
                        product.selectedSize : 
                        product[event.targetOption].defaultSize,
          selectedQuantity: 1
        }
      })"
></amp-selector>

此演示是在 amp-script 发布之前创建的。但是,这种逻辑在 JavaScript 中更容易编写和调试。对于具有更多业务逻辑的页面,使用 amp-script 将使您避免混淆并遵循更好的编程实践。

在许多情况下,您需要在同一页面上同时使用 amp-scriptamp-bind。对于更简单的交互,请部署 amp-bind,在您需要更多逻辑或结构时,请转向 amp-script。此外,尽管 amp-script 只能对其 DOM 子元素进行修改,如上所述,但它可以通过修改状态变量来影响页面的其余部分。 amp-bind 完成其余的工作,如此示例所示。

尽管当您的代码更改其可以访问的虚拟 DOM 时,WorkerDOM 会更改真实的 DOM,但不存在反向同步的机制。因此,不建议使用 amp-bind 或其他方式来修改 <amp-script> 的子元素。请将页面的该区域保留给您的 amp-script JavaScript。

amp-script 做贡献

amp-script 始终在发展,就像 AMP 一样。您可以通过参与来帮助改进它。思考其他开发人员可能也需要的新功能,提交问题,当然还有建议并贡献新功能