如果您已经制作了 AMP 网页,但尚未构建渐进式网页应用,那么您的 AMP 网页可以极大地简化您的渐进式网页应用的开发工作。本指南将向您介绍如何在渐进式网页应用中使用 AMP 以及如何将现有 AMP 网页用作数据源。

从 JSON 到 AMP

在最常见的情况下,渐进式网页应用是一款通过 Ajax 连接到 JSON API 的单页应用。随后,该 JSON API 会返回多组数据以支持导航,并会返回实际内容以呈现文章。

然后,您会继续操作,将原始内容转换成易用的 HTML,并将其呈现在客户端上。该过程不仅成本高昂,而且通常难以维护。因此,您不妨改为将自己现有的 AMP 网页重复用作内容来源。最棒的是:借助 AMP,您只需使用几行代码即可轻松实现这一点。

在渐进式网页应用中添加“Shadow AMP”

第一步是在您的渐进式网页应用中添加一种特殊版本的 AMP,称为“Shadow AMP”。对,就是这样 - 您在顶级网页中加载 AMP 库,但 AMP 库实际上并不会控制顶级内容。它只会按照您的要求,将我们网页的部分内容转换成对应的 AMP 版本。

请在您网页的标头中添加 Shadow AMP,所需代码如下所示:

<!-- Asynchronously load the AMP-with-Shadow-DOM runtime library. -->
<script async src="https://cdn.ampproject.org/shadow-v0.js"></script>

如何判断 Shadow AMP API 是否已可供使用?

我们建议您在设置好 async 属性的前提下加载 Shadow AMP 库。不过,这意味着您需要借助某种方法来判断该库是否已加载完毕且可供使用。

您所要观察的信号应该是全局 AMP 变量的可用性,并且 Shadow AMP 会使用“异步函数加载方法”来帮助您进行观察。您可以考虑使用如下代码:

(window.AMP = window.AMP || []).push(function(AMP) {
  // AMP is now available.
});

该代码会正常运行,而且以这种方式添加的任何数量的回调都确实会在 AMP 可用时触发,但为什么会这样呢?

该代码可实现以下操作:

  1. “如果 window.AMP 不存在,则创建一个空数组来占据其位置”
  2. “然后,将一个当 AMP 就绪时应被执行的回调函数推送到该数组中”

该代码之所以会正常运行,是因为 Shadow AMP 库在实际加载时会发现 window.AMP 下已有一个回调数组,然后便会处理整个队列。如果您日后再次执行同一函数,该代码仍会正常运行,因为 Shadow AMP 会使用其自身以及一种能立即触发回调的自定义 push 方法来替换 window.AMP

实现渐进式网页应用中的导航机制

您仍需手动实施此步骤。毕竟,如何在导航机制中展示内容链接是由您决定的。是以一些列表的形式?还是以一堆卡片的形式?

在常见的情况下,您需要获取某个 JSON,而此 JSON 需要能够返回多个包含些许元数据且依序排列的网址。最终,您应达到的结果是:每当用户点击其中任一链接时,便会触发相应的函数回调,并且此回调会包含所请求的 AMP 网页的网址。如果您能达到这样的结果,您就可以开始执行最后一个步骤了。

使用 Shadow AMP API 以内嵌的方式呈现网页

最后,当您想在用户操作后展示内容时,便可获取相关的 AMP 文档,并让 Shadow AMP 接管后续事宜。首先,实施一个函数以用于获取相应网页,所需代码与以下代码类似:

function fetchDocument(url) {

  // unfortunately fetch() does not support retrieving documents,
  // so we have to resort to good old XMLHttpRequest.
  var xhr = new XMLHttpRequest();

  return new Promise(function(resolve, reject) {
    xhr.open('GET', url, true);
    xhr.responseType = 'document';
    xhr.setRequestHeader('Accept', 'text/html');
    xhr.onload = function() {
      // .responseXML contains a ready-to-use Document object
      resolve(xhr.responseXML);
    };
    xhr.send();
  });
}

由于现在我们已经拥有了即时可用的 Document 对象,接下来就该让 AMP 接管并呈现此文档了。获取对作为 AMP 文档容器的 DOM 元素的引用,然后调用 AMP.attachShadowDoc(),所需代码如下所示:

// This can be any DOM element
var container = document.getElementById('container');

// The AMP page you want to display
var url = "https://my-domain/amp/an-article.html";

// Use our fetchDocument method to get the doc
fetchDocument(url).then(function(doc) {
  // Let AMP take over and render the page
  var ampedDoc = AMP.attachShadowDoc(container, doc, url);
});

大功告成!现在,您的 AMP 网页就会作为整个渐进式网页应用中的一个子级元素进行呈现了。

即时清理

很可能会发生的情况是:用户会在您的渐进式网页应用中从一个 AMP 网页导航到另一个 AMP 网页。在舍弃之前呈现的 AMP 网页时,请始终确保将此事告知 AMP,所需代码如下所示:

// ampedDoc is the reference returned from AMP.attachShadowDoc
ampedDoc.close();

这样即可让 AMP 知道您已不再使用此文档,同时也可释放内存并降低 CPU 开销。

了解实际运作方式

您可在我们制作的 React 样例中了解“AMP in PWA”模式的实际运作方式。该样例演示了导航过程中的顺利过渡情形,并附有一个将上述各步骤囊括在内的简单 React 组件。在该模式中,这两者(渐进式网页应用中灵活的自定义 JavaScript,以及 AMP)实现了强强联合,因为它们都能最大限度地发挥各自对内容加载速度的积极影响。

您还可查看一个使用了 Polymer 框架的 PWA + AMP 样例。该样例使用 amp-viewer 嵌入 AMP 网页。