AMP技术详解

4,181 阅读9分钟

AMP是什么

Google 前沿的 AMP 「 Accelerated Mobile Pages 」技术,能使用户从搜索引擎当中进入我们页面的体验得到一个极大的提升。

AMP组成

AMP HTML

AMP HTML 本质上是使用自定义 AMP 属性扩展的 HTML。最简单的 AMP HTML 文件如下所示:

<!doctype html>
<html ⚡>
 <head>
   <meta charset="utf-8">
   <link rel="canonical" href="hello-world.html">
   <meta name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1">
   <style amp-boilerplate>body{-webkit-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-moz-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-ms-animation:-amp-start 8s steps(1,end) 0s 1 normal both;animation:-amp-start 8s steps(1,end) 0s 1 normal both}@-webkit-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-moz-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-ms-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-o-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}</style><noscript><style amp-boilerplate>body{-webkit-animation:none;-moz-animation:none;-ms-animation:none;animation:none}</style></noscript>
   <script async src="https://cdn.ampproject.org/v0.js"></script>
 </head>
 <body>Hello World!</body>
</html>

AMP 网页通过 HTML 标记被搜索引擎和其他平台的<link rel=""> 发现。

AMP JS

AMP JS 库 可实现所有 AMP 的最佳性能做法、管理资源加载,并为您提供上面提到的自定义标记,所有这些都是为了确保快速渲染您的网页。

最重大的优化之一就是它可使来自外部资源的所有内容保持异步,让网页中的任何内容都能毫无阻碍地渲染。

其他性能技术还包括:将所有 iframe 沙盒化,加载资源之前对网页上每个元素的布局进行预先计算,以及禁用性能缓慢的 CSS 选择器。

AMP Cache

AMP 缓存是一种基于代理的内容传送网络 (CDN),用于传送有效的 AMP 文档。AMP 缓存旨在:

  1. 仅提供有效的 AMP 网页。
  2. 让 AMP 网页能够安全且高效地预加载。
  3. 对内容执行额外的性能优化措施,以提升用户体验。

谁会请求缓存的AMP页面?

缓存的 AMP 网页会被各类平台(如 Google 搜索、Google 新闻和 Cloudflare)和移动应用访问。移动应用可通过网址(请参阅 Google 的 AMP URL API)或通过渐进式网页应用中的跨源 XHR(详情请见嵌入 AMP 网页并将其用作数据源)关联到缓存的 AMP 内容。

如何实现?

平台可通过 <html ⚡><html amp> 标记发现您的 AMP 内容,进而缓存该内容。例如,Google 搜索会抓取内容;对于任何已被识别出的有效 AMP 网页,系统都会自动将相应内容添加到 Google AMP 缓存中。

AMP的好处

  1. 快速加载
  2. 减少页面复杂度
  3. Google搜索结果对AMP页面有预加载处理,更快地到达页面

AMP 如何提升性能

仅允许异步脚本(防止阻塞页面)

由于JavaScript也会阻塞 DOM 的构建,延缓页面渲染,AMP 仅允许异步 JavaScript。

AMP 页面不能包含作者自己编写的任何 JavaScript。 使用自定义 AMP 元素而不是 JavaScript 来处理互动页面功能。

例外: iframe 中允许第三方 JS,不过它不会阻塞渲染。

静态确定所有资源的大小(减少页面回流)

图片、广告或 iframe 等外部资源必须在 HTML 中声明其大小,以便 AMP 可以在资源下载前确定每个元素的大小和位置。

AMP 不必等待所有资源都下载完成就可以直接加载页面布局。

AMP 将文档布局与资源布局脱钩。 布局整个文档(包括字体)只需要一个 HTTP 请求。

由于 AMP 经过优化,可以在浏览器中避免会消耗大量资源的样式重新计算和布局,因此资源加载时不会存在任何重新布局。

不让扩展机制阻塞渲染(拓展组件不会阻塞渲染)

<script async custom-element="amp-iframe" src="https://cdn.ampproject.org/v0/amp-iframe-0.1.js"></script>

CSS 必须内嵌并具有大小限值(减少http请求)

CSS 会阻碍所有渲染和页面加载,并且往往变得臃肿。 在 AMP HTML 页面中,只允许内嵌样式。 与大多数网页相比,这一限制可从关键渲染路径中移除 1 个或多个 HTTP 请求。

另外,内嵌样式表最大为 50 KB。 虽然此大小对非常复杂的页面来说已经足够大,页面作者仍需要践行良好的 CSS 风格。

设定资源加载的优先级(懒加载)

AMP 可以控制所有资源下载:它会设定资源加载的优先级、仅加载需要的内容,以及预提取具有延迟加载特性的资源。

下载资源时,AMP 会优化下载,以便优先下载当前最重要的资源。

图片和广告仅在位于首屏、用户可能会查看或者快速滚动到它们时下载。

AMP 还会预提取具有延迟加载特性的资源。 资源加载尽可能晚,但预提取则尽可能早。 这样一来,加载速度非常快,不过只有在资源实际向用户显示时才会使用 CPU。

瞬时加载页面(预渲染首屏)

大量使用全新的 preconnect API,从而确保 HTTP 请求的速度尽可能快。

这样,在用户明确指明想要前往某个页面之前,该页面就可以渲染完成;在用户实际选择页面之前,页面就可能已经准备就绪,进而实现瞬时加载。

所有网络内容都可以应用预渲染,但这一过程也需要使用一些带宽和 CPU。 AMP 已经过优化,可以减少这两种因素的消耗。预渲染仅下载首屏资源,并且不会渲染可能要消耗大量 CPU 的资源。

在 AMP 文档进行预渲染以实现瞬时加载时,实际上只会下载首屏资源。

在 AMP 文档进行预渲染以实现瞬时加载时,不会下载可能要使用大量 CPU 的资源(例如第三方 iframe)。

如何进行AMP页面开发

  1. 创建页面

    <!doctype html>
    <html ⚡>
      <head>
        <meta charset="utf-8">
        <script async src="https://cdn.ampproject.org/v0.js"></script>
        <title>Hello AMP world</title>
        <link rel="canonical" href="hello-world.html">
        <meta name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1">
        <style amp-boilerplate>body{-webkit-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-moz-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-ms-animation:-amp-start 8s steps(1,end) 0s 1 normal both;animation:-amp-start 8s steps(1,end) 0s 1 normal both}@-webkit-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-moz-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-ms-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-o-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}</style><noscript><style amp-boilerplate>body{-webkit-animation:none;-moz-animation:none;-ms-animation:none;animation:none}</style></noscript>
      </head>
      <body>
        <h1>Hello AMP World!</h1>
      </body>
    </html>
    
    • 以文档类型 <!doctype html> 开头
    • 包含顶级 <html ⚡> 标记(也接受 <html amp>
    • 包含 <head><body> 标记(这些标记在 HTML 中是可选的)
    • <head>内包含一个 <link rel="canonical" href="$SOME_URL"> 标记,该标记指向 AMP HTML 文档的常规 HTML 版本,或在此类 HTML 版本不存在的情况下指向文档本身
    • 包含 <meta charset="utf-8"> 标记作为<head>标记的第一个子项
    • <head>标记内包含 <meta name="viewport" content="width=device-width,minimum-scale=1"> 标记。还建议包括 initial-scale=1
    • 包含 <script async src="https://cdn.ampproject.org/v0.js"></script> 标记作为<head>中的最后一个元素(这样做将会包括并加载 AMP JS 库)
    • 在其 <head> 标记内包含以下内容:
    <style amp-boilerplate>body{-webkit-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-moz-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-ms-animation:-amp-start 8s steps(1,end) 0s 1 normal both;animation:-amp-start 8s steps(1,end) 0s 1 normal both}@-webkit-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-moz-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-ms-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-o-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}</style><noscript><style amp-boilerplate>body{-webkit-animation:none;-moz-animation:none;-ms-animation:none;animation:none}</style></noscript>
    
  2. 向网页添加组件

    <amp-img src="https://www.ampproject.org/examples/images/amp.jpg"
      width="900" height="508" layout="responsive"></amp-img>
    
    <!-- this script is required for amp-youtube and must be in the <head> section  -->
    <script async custom-element="amp-youtube"
          src="https://cdn.ampproject.org/v0/amp-youtube-0.1.js"></script>
    <amp-youtube data-videoid="9Cfxm7cikMY"
        layout="responsive"
        width="480" height="270"></amp-youtube>
    
  3. 设计元素样式

    要为 AMP 网页上的元素设计样式,请向文档的 <head> 中名为 <style amp-custom> 的内嵌样式表添加 CSS:

    <style amp-custom>
      amp-img {
        margin: 0.5em;
      }
      body {
        max-width: 900px;
      }
    </style>
    
  4. 添加分析工具

<amp-analytics type="googleanalytics">
<script type="application/json">
{
  "vars": {
    "account": "UA-YYYY-Y"
  },
  "triggers": {
    "default pageview": {
      "on": "visible",
      "request": "pageview",
      "vars": {
        "title": "Name of the Article"
      }
    }
  }
}
</script>
</amp-analytics>
  1. 准备好页面分发

    在某些情况下,您可能希望同时拥有同一页面(例如,新闻文章)的非 AMP 版本和 AMP 版本。请考虑此情况:如果 Google 搜索找到该页面的非 AMP 版本,它如何知道该页面有 AMP 版本

    双页面:

    向非 AMP 页面中添加以下内容:

    <link rel="amphtml" href="https://www.example.com/url/to/amp/document.html">
    

    向 AMP 页面中添加以下内容

    <link rel="canonical" href="https://www.example.com/url/to/full/document.html">
    

单页面:

如果您只有一个页面,并且该页面是 AMP 页面,则您仍必须向其中添加规范链接,该链接只是指向自身:

<link rel="canonical" href="https://www.example.com/url/to/amp/document.html">

AMP的一些限制

  1. !important 限定符 禁止使用该限定符。 这是 AMP 网页能够强制实施其元素尺寸设定规则的必要条件。
  2. <link rel=”stylesheet”> 禁止使用,但 自定义字体除外。
  3. script标签. application/ld+json除外 更多规范和限制 https://www.ampproject.org/zh_cn/docs/fundamentals/spec

注意事项

  1. 做好跨域请求处理

    由于用户可能从AMP Cache访问你的网页, 所以访问的域名可能是某个cdn域名, 存在跨域的可能, 需要服务端做好相应配置 https://www.ampproject.org/zh_cn/docs/fundamentals/amp-cors-requests#verify-cors-header

    function assertCors(req, res, opt_validMethods, opt_exposeHeaders) {
     var unauthorized = 'Unauthorized Request';
     var origin;
     var allowedOrigins = [
        "https://example.com",
        "https://example-com.cdn.ampproject.org",
        "https://example.com.amp.cloudflare.com",
        "https://cdn.ampproject.org" ];
     var allowedSourceOrigin = "https://example.com";  //publisher's origin
     var sourceOrigin = req.query.__amp_source_origin;
    
     // If same origin
     if (req.headers['amp-same-origin'] == 'true') {
         origin = sourceOrigin;
     // If allowed CORS origin & allowed source origin
     } else if (allowedOrigins.indexOf(req.headers.origin) != -1 &&
         sourceOrigin == allowedSourceOrigin) {
         origin = req.headers.origin;
     } else {
         res.statusCode = 401;
         res.end(JSON.stringify({message: unauthorized}));
         throw unauthorized;
     }
    
     res.setHeader('Access-Control-Allow-Credentials', 'true');
     res.setHeader('Access-Control-Allow-Origin', origin);
     res.setHeader('Access-Control-Expose-Headers',
         ['AMP-Access-Control-Allow-Source-Origin']
             .concat(opt_exposeHeaders || []).join(', '));
     res.setHeader('AMP-Access-Control-Allow-Source-Origin', sourceOrigin);
    
    }