想在 AMP 網頁上做華麗麗效果的我

是否搞錯了什麼

想在 AMP 網頁上做華麗麗效果的我

是否搞錯了什麼

會  動  的

蔡孟穎 (Meng-Ying Tsai)

  • 又名文月、八盤
  • 現職為五倍紅寶石 web developer
  • ❤: 喝淺焙咖啡、唱日卡、嚐甜食

一年 365 天歡迎餵食,請多指教 ヽ(●´∀`●)ノ

起因?Why AMP?

做個網站,什麼都好但就是沒有流量

嘗試來做全站 AMP 化

為什麼要做 AMP ?

超絕快速的網頁效能

Bing / Google 會幫你 serve cache 頁面!

這麼好的話

為什麼大家不都來做 AMP ?

框架限制大、導入成本高

為什麼 AMP 網站會比較快

在 Critical Rendering Path 上做各種優化

限制東限制西

來個空白的 AMP page

<!doctype html>
<html amp lang="en">
  <head>
    <meta charset="utf-8">
    <script async src="https://cdn.ampproject.org/v0.js"></script>
    <title>My first AMP Page</title>
    <link rel="canonical" href="https://your.site/path">
    <meta name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1">
    <script type="application/ld+json">
    {
        "@context": "http://schema.org",
        "@type": "WebPage",
        "name": "My first AMP Page",
        "description": "Put some description of the page here"
    }
    </script>
    <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>My first AMP Page</h1>
    <p>Some content here!</p>
  </body>
</html>

來個空白的 AMP page

<!doctype html>
<html amp lang="en">
  <head>
    <meta charset="utf-8">
    <script async src="https://cdn.ampproject.org/v0.js"></script>
    <title>My first AMP Page</title>
    <link rel="canonical" href="https://your.site/path">
    <meta name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1">
    <script type="application/ld+json">
    {
        "@context": "http://schema.org",
        "@type": "WebPage",
        "name": "My first AMP Page",
        "description": "Put some description of the page here"
    }
    </script>
    <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>My first AMP Page</h1>
    <p>Some content here!</p>
  </body>
</html>

amp or

來個空白的 AMP page

<!doctype html>
<html amp lang="en">
  <head>
    <meta charset="utf-8">
    <script async src="https://cdn.ampproject.org/v0.js"></script>
    <title>My first AMP Page</title>
    <link rel="canonical" href="https://your.site/path">
    <meta name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1">
    <script type="application/ld+json">
    {
        "@context": "http://schema.org",
        "@type": "WebPage",
        "name": "My first AMP Page",
        "description": "Put some description of the page here"
    }
    </script>
    <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>My first AMP Page</h1>
    <p>Some content here!</p>
  </body>
</html>

來個空白的 AMP page

<!doctype html>
<html amp lang="en">
  <head>
    <meta charset="utf-8">
    <script async src="https://cdn.ampproject.org/v0.js"></script>
    <title>My first AMP Page</title>
    <link rel="canonical" href="https://your.site/path">
    <meta name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1">
    <script type="application/ld+json">
    {
        "@context": "http://schema.org",
        "@type": "WebPage",
        "name": "My first AMP Page",
        "description": "Put some description of the page here"
    }
    </script>
    <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>My first AMP Page</h1>
    <p>Some content here!</p>
  </body>
</html>

Canonical

JSDC 2019 - 先別管大亂鬥了, 你聽過 AMP 嗎?

來個空白的 AMP page

<!doctype html>
<html amp lang="en">
  <head>
    <meta charset="utf-8">
    <script async src="https://cdn.ampproject.org/v0.js"></script>
    <title>My first AMP Page</title>
    <link rel="canonical" href="https://your.site/path">
    <meta name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1">
    <script type="application/ld+json">
    {
        "@context": "http://schema.org",
        "@type": "WebPage",
        "name": "My first AMP Page",
        "description": "Put some description of the page here"
    }
    </script>
    <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>My first AMP Page</h1>
    <p>Some content here!</p>
  </body>
</html>

structure data

如何確定我的 AMP 頁面沒有問題?

使用 validation 工具 📖

如何確定我的 AMP 頁面沒有問題?

Browser Developer Console

https://你的網址#development=1

如何確定我的 AMP 頁面沒有問題?

Browser Developer Console

如何確定我的 AMP 頁面沒有問題?

Browser Extension

invalid

valid

cached

如何確定我的 AMP 頁面沒有問題?

Web Interface

如何確定我的 AMP 頁面沒有問題?

Web Interface

如何確定我的 AMP 頁面沒有問題?

VS Code extension

如何確定我的 AMP 頁面沒有問題?

NPM Package for CI 📖

'use strict';
var amphtmlValidator = require('amphtml-validator');
var fs = require('fs');

amphtmlValidator.getInstance().then(function (validator) {
  var input = fs.readFileSync('index.html', 'utf8');
  var result = validator.validateString(input);
  ((result.status === 'PASS') ? console.log : console.error)(result.status);
  for (var ii = 0; ii < result.errors.length; ii++) {
    var error = result.errors[ii];
    var msg = 'line ' + error.line + ', col ' + error.col + ': ' + error.message;
    if (error.specUrl !== null) {
      msg += ' (see ' + error.specUrl + ')';
    }
    ((error.severity === 'ERROR') ? console.error : console.warn)(msg);
  }
});

如何確定我的 AMP 頁面沒有問題?

Command Line Tool

npm install -g amphtml-validator
amphtml-validator https://amp.dev/
#=> https://amp.dev/: PASS

AMP 頁面沒有過 validate 會怎樣

不會怎樣

 

沒有 Google / Bing Cache

沒有對應的 search result

Google Search Console 會叫

現在我們來把剛剛那個空白頁面拿來 validate

<!doctype html>
<html amp lang="en">
  <head>
    <meta charset="utf-8">
    <script async src="https://cdn.ampproject.org/v0.js"></script>
    <title>My first AMP Page</title>
    <link rel="canonical" href="https://your.site/path">
    <meta name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1">
    <script type="application/ld+json">
    {
        "@context": "http://schema.org",
        "@type": "WebPage",
        "name": "My first AMP Page",
        "description": "Put some description of the page here"
    }
    </script>
    <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>My first AMP Page</h1>
    <p>Some content here!</p>
  </body>
</html>

亮綠燈

那我來放個圖片

<h1>My first AMP Page</h1>
<p>Some content here!</p>
<img src="https://source.unsplash.com/kAiyiesI_Kk/800x600" alt="a lovely fox" />

亮紅燈

HAIYAAAAAA

USE MSG! 必須改成 amp-img

AMP 要求你必須使用 component 「amp-img

它會幫你:

  • 給定區塊,避免 content reflow
  • 幫你做 lazy loading

必須改成 amp-img

<amp-img layout='fixed' 
         width='800' 
         height='600' 
         src="https://source.unsplash.com/kAiyiesI_Kk/800x600" 
         alt="a lovely fox" 
/>

跟平常的 <img>比起來多了 layout 的部分

Layouts

為了守護世界的正義避免 content reflow

以及讓 asset 還沒載回來就可以 render layout

 

幾乎所有的 AMP component 都需要指定 layout

常見 Layouts

fixed

intrinsic

responsive

常見 Layouts

fill

fixed-height

fiex-item

為了維護 layout 不被破壞

AMP 規定 CSS 不能用 !important

<amp-img
  alt="A image of ..."
  src="image.jpg"
  width="900"
  height="600"
  layout="responsive"
>
</amp-img>
<amp-img 
fallback="" 
alt="A image of ..." 
class="i-amphtml-layout-responsive i-amphtml-layout-size-defined i-amphtml-element i-amphtml-layout" 
src="image.jpg" 
layout="responsive" 
width="900" 
height="600" 
i-amphtml-layout="responsive">
  <i-amphtml-sizer slot="i-amphtml-svc" style="padding-top: 66.6667%;"></i-amphtml-sizer>
  <img decoding="async" alt="A image of ..." src="image.jpg" class="i-amphtml-fill-content i-amphtml-replaced-content">
</amp-img>

amp-runtime 時 amp-img 會長出真 img

耶 又亮綠燈惹

開開心心 🎶

來點 CSS

<link href=".../style.css" rel="stylesheet">
.m-auto {
  margin: auto;
}

/* ... */

亮紅燈

咦咦咦咦?我又做錯什麼了嗎?!

AMP 不允許外部 CSS

AMP 希望一個 request 裡就可以獲得所有 CSS 規則

只允許一個 <style> taginline CSS

<style amp-custom>
.m-auto {
  margin: auto;
}

/* ... */
</style>

amp-custom 要記得加哦

耶 又亮綠燈惹

好歐

來點 Javascript

<p>FQDN 最長可以幾個 bytes?</p>
<button class="js-see-clue">看提示</button>
<p class='js-clue-text hidden'>大於 250 小於 255 的數字</p>

<script>
clueButton = document.querySelector('.js-see-clue')
clueText = document.querySelector('.js-clue-text')
clueButton.addEventListener('click', function (event) {
  event.preventDefault();
  
  clueText.classList.remove('hidden')
}, false);
</script>

亮紅燈

哪泥!又亮!

AMP 不允許你隨心所欲放 Script

平常會用 JS 做的事情必須被 AMP 元件所取代

 

除非大量使用 amp-script ,

要不然你可能很久都不會碰到 javascript

AMP 不允許你隨心所欲放 Script

廣告 →  amp-ad

圖片輪播 → amp-carousel

 

AMP 不允許你隨心所欲放 Script

那如果沒有適合我的 component 呢?

接下來你需要跟這些小夥伴做朋友!

接下來你需要跟這些小夥伴做朋友!

amp-bind

amp-script

amp-iframe

amp-list

改寫改寫改寫....

<p>FQDN 最長可以幾個 bytes?</p>
<button on="tap:clueText.show">看提示</button>
<div id='clueText' hidden>大於250 小於 255 的數字</div>

版本 1:純 events & actions 版

耶 亮綠燈!

(心)

Events

  • tap

Actions

  • hide
  • show
  • toggleVisibility
  • toggleClass(class=STRING, force=BOOLEAN)
  • scrollTo(duration=INTEGER, position=STRING)

AMP 頁面上的 element 都適用以下的 events & actions

<div id="clueText" hidden>

大於250 小於 255 的數字

</div>

element ID

<button on="tap:clueText.show">

看提示

</button>

event

action

用另一種方式改寫改寫....

<!-- in your head-->
<script async custom-element="amp-bind" src="https://cdn.ampproject.org/v0/amp-bind-0.1.js"></script>

<!-- in your <body> -->
<p>FQDN 最長可以幾個 bytes?</p>
<button on="tap:AMP.setState({ hideClue: false })">看提示</button>
<div hidden [hidden]="hideClue">大於250 小於 255 的數字</div>

版本 2:amp-bind 版

耶 又亮綠燈惹

終於 耶

何謂 amp-bind?

淘氣頑皮的小波 (amp-bind),

擅長透過改變 state 來製造

充滿 interaction 的 AMP 網頁。

amp-bind 起手式

記得 require 對應的 script

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

除了 amp-img 以外的 amp component,

都要記得這個 require 進來

<div hidden [hidden]="hideClue">大於250 小於 255</div>

binding

<button on="tap:AMP.setState({ hideClue: false })">

  看提示

</button>

event

action

等同 data-amp-bind-text

amp-bind 起手式

[text]

[src]

[disabled]

[class]

[width]

[height]

[hidden]

[aria-label]

👉 Binding Types 👈

amp-bind 可以拿來綁哪些東西?

amp-state 可以定義 initial state 嗎?

在可以跟不可以之間

amp-state 可以定義 initial state 嗎?

你可以先定義起來

但只有在 setState 發生後才會有效

<amp-state id="info">
  <script type="application/json">
    "按下按鈕會看到這行"
  </script>
</amp-state>

<div [text]="info">還沒按按鈕看到的是這行</div>
<button on="tap:AMP.setState({})">Set state!</button>

amp-state 可以定義 initial state 嗎?

JSON 資料的來源也可以是某個 remote endpint

<amp-state id="info" src="https://my-site.blah/items.json">
</amp-state>

amp-state 有 concat, join...之類的 functions 嗎

有owo/

  • Array: concat, filter, includes, indexOf, join...
  • Number: toExponential, toFixed, toString...
  • String: charAt, charCodeAt, concat, indexOf, replace...
  • Math: abs, cell, floor, max, min, pow...
  • Object: keys, values
  • Global: encodeURI, encodeURIComponent​

amp-state 有 concat, join...之類的 functions 嗎

<amp-state id="products">
  <script type="application/json">
    [
      {"id": 1, "name": "會讓你很開心的草", "price": 81000},
      {"id": 2, "name": "讓人很迷幻的蘑菇", "price": 14000}
    ]
  </script>
</amp-state>
<p>點一下按鈕看看本店提供什麼產品OwO</p>
<div [text]="products.map((obj, i) => obj.name).join('、')"></div>
<button on="tap:AMP.setState({})">Click me</button>

如何用 AMP 做各種功能?

favorite button?

來看看官方範例

favorite button?

我們有兩個範例 endpoint

 

GET /favorite: 

回傳是否被加入我的最愛,true 代表有加入,false 代表沒加入

 

POST /favorite: 

對他送 true / false 可以改變「我的最愛」的狀態

 

favorite button?

當按鈕被按就送 xhr request 出去(X)

放個 form,把愛心鈕設計成 submit 按鈕(O)

favorite button?

先放個 AMP state

<amp-state id="favorite"
  credentials="include"
  src=".../favorite">
</amp-state>

favorite button?

<form class="favorite-button"
  method="post"
  action-xhr=".../favorite"
  target="_top"
  on="submit:AMP.setState({
      favorite: !favorite
    });
  submit-error:AMP.setState({
      favorite: !favorite
    })">
  <!-- form content... -->
</form>

先準備一個 form

favorite button?

當按下 submit 時會改變 favorite 的 state,

失敗時再把它變回來

<form class="favorite-button"
  method="post"
  action-xhr=".../favorite"
  target="_top"
  on="submit:AMP.setState({
      favorite: !favorite
    });
  submit-error:AMP.setState({
      favorite: !favorite
    })">
  <!-- form content... -->
</form>

favorite button?

你說:我不記得 <form>有 action-xhr 這個 attr 啊?

 

你講得沒錯,因為這是個 amp-form

<form class="favorite-button"
  method="post"
  action-xhr="..../favorite"
  target="_top"
  on="submit:AMP.setState({
      favorite: !favorite
    });
  submit-error:AMP.setState({
      favorite: !favorite
    })">
  <!-- form content... -->
</form>

何謂 amp-form?

和普通的吸塵器 (form) 很像

只是多了一些功能跟特色。

  • 讓你可以送 AJAX request 的 action-xhr
  • verify 表單功能
  • polyfills / 補上 missing behavior
  • 其他 action、event、attributes...

何謂 amp-form?

amp-form 一些奇妙的限制

不允許 non-XHR POST

不允許 PUT, PATCH, DELETE

 

related issue: #27368, #10408

favorite button?

<div class="favorite-button">
  <amp-list
    width="56"
    height="56"
    credentials="include"
    items="."
    single-item
    src="../favorite"
    binding="always"
  >
    <template type="amp-mustache">
      <input
        type="submit"
        class="{{#.}}heart-fill{{/.}}{{^.}}heart-border{{/.}}"
        [class]="favorite ? 'heart-fill' : 'heart-border'"
        value
        aria-label="Favorite Toggle"
      />
    </template>
  </amp-list>
</div>

然後我們在 form 裡面放一個 amp-list

何謂 amp-list?

開心愛笑的拉拉 (amp-list),沒有他 AMP 網頁就會失去活力。

何謂 amp-list?

還記得 Google / Bing cache 嗎?

 

為了確保你的頁面上的資訊是的,

必須用 amp-list 來撈資料

沒有 amp-list 可能會這樣...

www.google.com/amp/s/mysite.blah

你想買的辣個酷東西

現在只要 $81000

緊張!存貨只剩 666

mysite.blah

你想買的商品

已經下架了呦 O.<

click 買!

買!

「庫存不是還有 666 嗎?」

買不到酷東西的民眾會森七七

amp-list 起手式

記得 require 對應的 script

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

amp-list

amp-mustache

何謂 amp-mustache?

一顆變幻無窮 & 非常實用的

橘色球球 (amp-mustache),

經常與拉拉 (amp-list) 和其他夥伴一起出現。

amp-list 起手式

<amp-list layout="fixed-height"
  height="300"
  src="/products.json"
  binding="no">
  <template type="amp-mustache">
      <a href="{{url}}">{{name}} ${{price}}</a>
  </template>
</amp-list>
{
  "items": [
    {
      "id": 1,
      "name": "Apple",
      "price": "1.99",
      "url": "#",
    },
    {
      "id": 2,
      "name": "Orange",
      "price": "0.99",
      "url": "#",
    },
  ]
}

 /products.json

 /index.html

會把 template 裡的內容根據給的資料 render 出來

src 指定從哪裡 fetch json 

{{myVariable}}

get value

conditionals

{{#heartfill}}{{/heartfill}}

negative

conditionals

{{^heartfill}}{{/heartfill}}

loop

{{#cart_items}}

    <li>{{name}}: ${{price}}</li>{{/cart_items}}

amp-list 會根據資料型態判斷決定 {{#blah}}{{/blah}} 是條件判斷還是 loop

amp-mustache 語法大全

回去看剛剛的 favorite button 範例

<div class="favorite-button"> <!-- form -->
  <amp-list
    width="56"
    height="56"
    credentials="include"
    items="."
    single-item
    src="../favorite"
    binding="always"
  >
    <template type="amp-mustache">
      <input
        type="submit"
        class="{{#.}}heart-fill{{/.}}{{^.}}heart-border{{/.}}"
        [class]="favorite ? 'heart-fill' : 'heart-border'"
        value
        aria-label="Favorite Toggle"
      />
    </template>
  </amp-list>
</div>

回去看剛剛的 favorite button 範例

<div class="favorite-button">
  <amp-list
    width="56"
    height="56"
    credentials="include"
    items="."
    single-item
    src="../favorite"
    binding="always"
  >
    <template type="amp-mustache">
      <input
        type="submit"
        class="{{#.}}heart-fill{{/.}}{{^.}}heart-border{{/.}}"
        [class]="favorite ? 'heart-fill' : 'heart-border'"
        value
        aria-label="Favorite Toggle"
      />
    </template>
  </amp-list>
</div>

回去看剛剛的 favorite button 範例

class="{{#.}}heart-fill{{/.}}{{^.}}heart-border{{/.}}"
[class]="favorite ? 'heart-fill' : 'heart-border'"

/items/1/favorite 回傳內容

false

amp-list 根據從 endpoint 拿回來的值決定 class 是 heart-fill 或 heart-border

回去看剛剛的 favorite button 範例

class="{{#.}}heart-fill{{/.}}{{^.}}heart-border{{/.}}"
[class]="favorite ? 'heart-fill' : 'heart-border'"

AMP.printState()

true

當 "favorite" 這個 AMP state 被改變時,根據它的值決定 class 是 heart-fill 還是 heart-border

加點 CSS

<style amp-custom>
  .favorite-button input[type="submit"] {
    width: 48px;
    height: 48px;
    cursor: pointer;
    border: none;
  }
  .favorite-button .heart-fill {
    background: url('data:image/svg+xml;utf8,<svg fill="%23000000" height="48" viewBox="0 0 24 24" width="48" xmlns="http://www.w3.org/2000/svg"><path d="M0 0h24v24H0z" fill="none"/><path d="M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z"/></svg>');
  }
  .favorite-button .heart-border {
    background: url('data:image/svg+xml;utf8,<svg fill="%23000000" height="48" viewBox="0 0 24 24" width="48" xmlns="http://www.w3.org/2000/svg"><path d="M0 0h24v24H0z" fill="none"/><path d="M16.5 3c-1.74 0-3.41.81-4.5 2.09C10.91 3.81 9.24 3 7.5 3 4.42 3 2 5.42 2 8.5c0 3.78 3.4 6.86 8.55 11.54L12 21.35l1.45-1.32C18.6 15.36 22 12.28 22 8.5 22 5.42 19.58 3 16.5 3zm-4.4 15.55l-.1.1-.1-.1C7.14 14.24 4 11.39 4 8.5 4 6.5 5.5 5 7.5 5c1.54 0 3.04.99 3.57 2.36h1.87C13.46 5.99 14.96 5 16.5 5c2 0 3.5 1.5 3.5 3.5 0 2.89-3.14 5.74-7.9 10.05z"/></svg>');
  }
</style>

favorite button 就完成惹!

amp-bind + amp-list 的組合

基本上可以完成大部分的需求

雖然都參雜在 html 裡真的頗醜w 

沒有辦法只用 amp-bind 和 amp-list

做出來的東西怎麼辦?

例如:操作 localStorage?

AMP 沒有提供

專門讓你直接操作 localStorage 的元件

操作 localStorage?

你可能會想用 localStorage 處理的東西

  • 逼人 accept cookie 的小窗窗
  • 「首購打折!」的小提醒
  • 表單內容暫存

操作 localStorage?

你可能會想用 localStorage 處理的東西

  • 逼人 accept cookie 的小窗窗
  • 「首購打折!」的小提醒
  • 表單內容暫存

amp-user-notification

amp-script

amp-consent

amp-consent vs amp-user-notification

amp-consent:

  • 使用者可以決定同意、不同意,選擇後可以更改
  • 可以根據選擇狀態 block 頁面上的區塊

amp-user-notification

  • 使用者 dismiss 通知後就不在顯示

amp-consent

require script

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

amp-consent

  <amp-consent id="myUserConsent" layout="nodisplay">
    <script type="application/json">{
      "consentInstanceId": "consent-id",
      "consentRequired": true,
      "promptUI": "consentDialog",
      "postPromptUI": "post-consent-ui"
    }</script>
    <div class="popupOverlay" id="consentDialog">
      <div class="consentPopup">
        <div class="dismiss-button" role="button" tabindex="0" on="tap:myUserConsent.dismiss">X</div>
        <h2>隱私選項</h2>
        <p>我們使用 Cookie 等技術來提供個人化內容,讓您有更順暢的使用體驗。</p>
        <p>點擊「OK」即代表你同意我們在《Cookie 政策》中列出的內容。</p>
        <button on="tap:myUserConsent.accept">接受</button>
        <button on="tap:myUserConsent.reject">拒絕</button>
      </div>
    </div>
    <div id="post-consent-ui">
      <button on="tap:myUserConsent.prompt()">點我修改隱私選項</button>
    </div>
  </amp-consent>

amp-consent

consent configuration

{
  "consentInstanceId": :"consent-id",
  "consentRequired": true,
  "promptUI": "consentDialog",
  "postPromptUI": "post-consent-ui"
}

amp-user-notification

require script

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

起手式

<amp-user-notification
  layout="nodisplay"
  id="firstVisitNotification"
>
  歡迎你來OO購物,新朋友有首購八折優惠哦!
  <button on="tap:firstVisitNotification.dismiss">我知道了</button>
</amp-user-notification>

何謂 amp-script

饒了我吧...我已經掰不出來了 _(´ཀ`」 ∠)_ 

喜歡散步玩耍的丁丁,

透過允許部分 javascript 來增添

amp 網頁的豐富與變化

amp-script 起手式

require script

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

amp-script 起手式

外部 script

<amp-script layout="container"
  src=".../hello-world.js"
  >
  <button id="hello-url">Say hello!</button>
</amp-script>

amp-script 起手式

<meta name="amp-script-src" content="sha384-2YHMXBviHGAmLLcv-pdH0stSWCuPV3nnNotu8EEFHMDoINTJAE3K4ROjMMsMRcGZ ">

inline script

<amp-script layout="container"
  src="helloWorld"
  >
  <button id="hello-url">Say hello!</button>
</amp-script>

<script id="helloWorld">
  const button = document.getElementById('hello-url');

  button.addEventListener('click', () => {
    const h1 = document.createElement('h1');
    h1.textContent = 'Hello World!';
    document.body.appendChild(h1);
  });
</script>

加上 hash

確定這些 script 是你加的,不是黑黑加的 👻

👉 allowed APIs 👈

Element.querySelector 🔺 (Partial support)

document.querySelector 

Element.innerHTML

Event.preventDefault 

HTMLElement.dataset  

HTMLElement.innerText  

 

amp-script 支援哪些 API

amp-script

  • 每個 <amp-script> 有10 KB 的大小限制 
  • JS 大小總共不能超過 150 KB (real size)
  • amp-script 不能巢狀
  • 只允許 create amp-img amp-layout 

requires user gestures to change page content.

amp-script 做表單暫存

<amp-script 
  width="200" 
  height="500" 
  script="storeFormData" 
  sandbox="allow-forms">
  <div class="sectionWrapper">  
    <form class="userInfoForm" action="POST" target="_top">
      <div class="inputRow">
        <label for="name">名稱</label>
        <input type="text" id="name" name="name" />
      </div>
      <div class="inputRow">
        <label for="address">地址</label>
        <input type="text" id="address" name="address" />
      </div>
    </form>
    <button class="updateUserInfo">更新 localStorage</button>
  </div>  
</amp-script>

amp-script 做表單暫存

<script id="storeFormData" type="text/plain" target="amp-script">
  const userData = JSON.parse(window.localStorage.getItem('user'));
  if(userData){
    document.getElementById("name").value = userData.name
    document.getElementById("address").value = userData.address
  }
  const updateUserInfoBtn = document.querySelector('.updateUserInfo')
  updateUserInfoBtn.addEventListener('click', function(e) {
    let newUserData = {
      name: document.getElementById("name").value,
      address: document.getElementById("address").value,
    }
    window.localStorage.setItem('user', JSON.stringify(newUserData));
  });
</script>

第三方 plugin

amp-script or amp-iframe

什麼時候會想用到 iframe?

facebook

instagram

twitter

什麼時候會想用到 iframe?

facebook

instagram

twitter

amp-facebook

amp-instagram

amp-twitter

何謂 amp-iframe

有人可以幫我想一下介紹詞嗎 QQ

毫無反應,就是隻迪西

amp-iframe

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

<!-- in your body -->
<amp-iframe layout='fixed' 
            width='304' 
            height='154'  
            src="https://yourSite.blah">
  <div placeholder> loading... </div>
</amp-iframe>

amp-bind 組到東西很醜

tap: AMP.setState({myState: blah.map({}).join('....'),
                   anotherState.reduce({...})})

amp-bind 組到東西很醜

amp-bind-macro

amp-bind-macro 神奇整理術

require amp-bind 就可以用 amp-bind-macro

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

amp-bind-macro 神奇整理術

<amp-state id="myNumbers">
  <script type="application/json">
    [5, 10, 22, 3, 5, 111, 7, 6, 3]
  </script>
</amp-state>

<p [text]="myNumbers.reduce((sum, count) => (sum + count), 0)"></p>
<button on="tap:AMP.setState({})">set state</button>

BEFORE 😑

amp-bind-macro 神奇整理術

<amp-bind-macro 
  arguments="array_of_numbers"
  expression="array_of_numbers.reduce((sum, count) => (sum + count), 0)" 
  id="getSum"></amp-bind-macro>

<amp-state id="myNumbers">
  <script type="application/json">
    [5, 10, 22, 3, 5, 111, 7, 6, 3]
  </script>
</amp-state>

<p [text]="getSum(myNumbers)"></p>
<button on="tap:AMP.setState({})">set state</button>

AFTER 😃

用到走火入魔的時候,整理術還是不管用 :P

amp-bind 複雜度限制

amp-bind 的 JSON 資料大小限制 

小小地調查一下

有在做 AMP 網頁?

小小地調查一下

聽完了以後覺得不想做 AMP?

智慧鯊魚開示

我討厭 AMP

Pros & Cons

相當好的頁面效能

豐富的 component  可以使用

AMP Cache!

 

難客製化

難維護

限制一大堆

開發成本激增

....

 

開發體驗非常糟

Two Questions

怎麼開發 AMP 才不會這麼痛苦?

 

AMP 有未來嗎?

  怎麼開發 AMP 才不會這麼痛苦

 Next.js

11ty

wordpress

  怎麼開發 AMP 才不會這麼痛苦

拿 amp-script 去搭其他前端框架!

eg.

 AMP 有未來嗎? 

做 AMP 網站的困境

  • 我要的功能沒有相對應的 component
  • 但 amp-bind 讓我的 html code 好混亂
  • 我很難在 AMP 裡面用 React、Vue 等框架
  • 就是那一兩個地方沒辦法過 validation

 AMP 有未來嗎? 

AMP 全家桶

 AMP 有未來嗎? 

Bento AMP

 AMP 有未來嗎? 

Bento AMP

AMP

Non-AMP

AMP

Non-AMP

 AMP 有未來嗎? 

AMP Fest 2020 - What's Next In AMP?

 AMP 有未來嗎? 

 Bento AMP → Valid AMP → Cached AMP

Thanks for listening (ゝ∀・)⌒☆