想在 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>

來個空白的 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

Canonical

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

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

使用 validate 工具 📖

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

Browser Developer Console

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

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

Browser Developer Console

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

Web Interface

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

Web Interface

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

VS Code extension

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

NPM Package for CI

https://www.npmjs.com/package/amphtml-validator

如何確定我的 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

亮紅燈

哪泥!!!!why?

⚡ : The tag 'img' may only appear as a descendant of tag 'noscript'. Did you mean 'amp-img'? : https://amp.dev/documentation/components/amp-img/

必須改成 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;
}

/* ... */

亮紅燈

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

⚡ : The attribute 'href' in tag 'link rel=stylesheet for fonts' is set to the invalid value '/styles.css'.

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 出去(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

 

related issue: #27368

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="/items.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">
  <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

<!-- 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('...').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-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>

用到走火入魔的時候,整理術還是不管用 :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 去搭其他前端框架!

 AMP 有未來嗎? 

做 AMP 網站的困境

我要的功能沒有相對應的 component

但 amp-bind 讓我的 html code 好混亂

 AMP 有未來嗎? 

做 AMP 網站的困境

我很難在 AMP 裡面用 React、Vue 等框架

 AMP 有未來嗎? 

做 AMP 網站的困境

就是那一兩個功能做不出來,就是有地方沒辦法過 validation

 AMP 有未來嗎? 

為什麼會有這樣的困境?

AMP 全家桶

 AMP 有未來嗎? 

Bento AMP 

Thanks for listening (ゝ∀・)⌒☆

想在 AMP 網頁上做華麗麗效果的我是否搞錯了什麼

By Meng-Ying Tsai

想在 AMP 網頁上做華麗麗效果的我是否搞錯了什麼

  • 31
Loading comments...

More from Meng-Ying Tsai