想在 AMP 網頁上做華麗麗效果的我
是否搞錯了什麼
想在 AMP 網頁上做華麗麗效果的我
是否搞錯了什麼
會 動 的
蔡孟穎 (Meng-Ying Tsai)
- 又名文月、八盤
- 現職為五倍紅寶石 web developer
- ❤: 喝淺焙咖啡、唱日卡、嚐甜食
一年 365 天歡迎餵食,請多指教 ヽ(●´∀`●)ノ
![](https://s3.amazonaws.com/media-p.slid.es/uploads/427531/images/5824810/52417160_2131728573562551_6746436428424544256_n.jpg)
起因?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
![](https://s3.amazonaws.com/media-p.slid.es/uploads/427531/images/7573597/螢幕快照_2020-07-21_下午6.17.51.png)
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
![](https://s3.amazonaws.com/media-p.slid.es/uploads/427531/images/7758992/pasted-from-clipboard.png)
如何確定我的 AMP 頁面沒有問題?
Browser Extension
![](https://s3.amazonaws.com/media-p.slid.es/uploads/427531/images/7573872/pasted-from-clipboard.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/427531/images/7573874/pasted-from-clipboard.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/427531/images/7573876/pasted-from-clipboard.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/427531/images/7860303/螢幕快照_2020-10-24_上午10.29.49.png)
invalid
valid
cached
如何確定我的 AMP 頁面沒有問題?
Web Interface
![](https://s3.amazonaws.com/media-p.slid.es/uploads/427531/images/7758998/螢幕快照_2020-09-24_下午6.32.54.png)
如何確定我的 AMP 頁面沒有問題?
![](https://s3.amazonaws.com/media-p.slid.es/uploads/427531/images/7759003/螢幕快照_2020-09-24_下午6.35.54.png)
Web Interface
如何確定我的 AMP 頁面沒有問題?
VS Code extension
![](https://s3.amazonaws.com/media-p.slid.es/uploads/427531/images/7759018/螢幕快照_2020-09-24_下午6.40.22.png)
如何確定我的 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" />
亮紅燈
![](https://s3.amazonaws.com/media-p.slid.es/uploads/427531/images/7819723/pasted-from-clipboard.png)
HAIYAAAAAA
USE MSG! 必須改成 amp-img
AMP 要求你必須使用 component 「amp-img」
它會幫你:
- 給定區塊,避免 content reflow
- 幫你做 lazy loading
![](https://s3.amazonaws.com/media-p.slid.es/uploads/427531/images/7861125/pasted-from-clipboard.png)
必須改成 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
![](https://s3.amazonaws.com/media-p.slid.es/uploads/427531/images/7768290/amp-fixed-320.gif)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/427531/images/7768293/amp-responsive-320.gif)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/427531/images/7768298/amp-intrinsic-3202.gif)
常見 Layouts
fill
fixed-height
fiex-item
![](https://s3.amazonaws.com/media-p.slid.es/uploads/427531/images/7768539/amp-flex-item-320.gif)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/427531/images/7768542/amp-fill-320.gif)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/427531/images/7768551/amp-height.gif)
為了維護 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> tag 和 inline 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>
亮紅燈
哪泥!又亮!
![](https://s3.amazonaws.com/media-p.slid.es/uploads/427531/images/7820194/pasted-from-clipboard.png)
AMP 不允許你隨心所欲放 Script
平常會用 JS 做的事情必須被 AMP 元件所取代
除非大量使用 amp-script ,
要不然你可能很久都不會碰到 javascript
AMP 不允許你隨心所欲放 Script
廣告 → amp-ad
圖片輪播 → amp-carousel
AMP 不允許你隨心所欲放 Script
那如果沒有適合我的 component 呢?
接下來你需要跟這些小夥伴做朋友!
接下來你需要跟這些小夥伴做朋友!
![](https://s3.amazonaws.com/media-p.slid.es/uploads/427531/images/7768747/pasted-from-clipboard.png)
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 網頁。
![](https://s3.amazonaws.com/media-p.slid.es/uploads/427531/images/7769039/pasted-from-clipboard.png)
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?
![](https://s3.amazonaws.com/media-p.slid.es/uploads/427531/images/7768943/pasted-from-clipboard.png)
和普通的吸塵器 (form) 很像
只是多了一些功能跟特色。
- 讓你可以送 AJAX request 的 action-xhr
- verify 表單功能
- polyfills / 補上 missing behavior
- 其他 action、event、attributes...
何謂 amp-form?
![](https://s3.amazonaws.com/media-p.slid.es/uploads/427531/images/7768943/pasted-from-clipboard.png)
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?
![](https://s3.amazonaws.com/media-p.slid.es/uploads/427531/images/7768962/pasted-from-clipboard.png)
開心愛笑的拉拉 (amp-list),沒有他 AMP 網頁就會失去活力。
何謂 amp-list?
![](https://s3.amazonaws.com/media-p.slid.es/uploads/427531/images/7768962/pasted-from-clipboard.png)
還記得 Google / Bing cache 嗎?
為了確保你的頁面上的資訊是新的,
必須用 amp-list 來撈資料
沒有 amp-list 可能會這樣...
www.google.com/amp/s/mysite.blah
你想買的辣個酷東西
現在只要 $81000
緊張!存貨只剩 666
![](https://media2.giphy.com/media/3oKIPEnIVoeC3iq1Y4/giphy.gif)
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) 和其他夥伴一起出現。
![](https://s3.amazonaws.com/media-p.slid.es/uploads/427531/images/7772441/pasted-from-clipboard.png)
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 網頁的豐富與變化
![](https://s3.amazonaws.com/media-p.slid.es/uploads/427531/images/7822712/pasted-from-clipboard.png)
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?
![](https://s3.amazonaws.com/media-p.slid.es/uploads/427531/images/7836627/螢幕快照_2020-10-18_下午6.59.10.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/427531/images/7836631/螢幕快照_2020-10-18_下午7.01.20.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/427531/images/7836637/螢幕快照_2020-10-18_下午7.03.33.png)
什麼時候會想用到 iframe?
![](https://s3.amazonaws.com/media-p.slid.es/uploads/427531/images/7836627/螢幕快照_2020-10-18_下午6.59.10.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/427531/images/7836631/螢幕快照_2020-10-18_下午7.01.20.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/427531/images/7836637/螢幕快照_2020-10-18_下午7.03.33.png)
amp-facebook
amp-instagram
amp-twitter
何謂 amp-iframe
有人可以幫我想一下介紹詞嗎 QQ
毫無反應,就是隻迪西
![](https://s3.amazonaws.com/media-p.slid.es/uploads/427531/images/7860309/pasted-from-clipboard.png)
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 組到東西很醜
![](https://s3.amazonaws.com/media-p.slid.es/uploads/427531/images/7836697/pasted-from-clipboard.png)
tap: AMP.setState({myState: blah.map({}).join('....'),
anotherState.reduce({...})})
amp-bind 組到東西很醜
amp-bind-macro
![](https://s3.amazonaws.com/media-p.slid.es/uploads/427531/images/7796051/pasted-from-clipboard.png)
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
![](https://s3.amazonaws.com/media-p.slid.es/uploads/427531/images/7579426/螢幕快照_2020-07-23_下午12.34.47.png)
amp-bind 複雜度限制
![](https://s3.amazonaws.com/media-p.slid.es/uploads/427531/images/7565014/螢幕快照_2020-07-14_上午11.29.33.png)
amp-bind 的 JSON 資料大小限制
小小地調查一下
有在做 AMP 網頁?
小小地調查一下
聽完了以後覺得不想做 AMP?
智慧鯊魚開示
![](https://s3.amazonaws.com/media-p.slid.es/uploads/427531/images/7836712/pasted-from-clipboard.png)
我討厭 AMP
Pros & Cons
相當好的頁面效能
豐富的 component 可以使用
AMP Cache!
![](https://s3.amazonaws.com/media-p.slid.es/uploads/427531/images/7603350/pasted-from-clipboard.png)
難客製化
難維護
限制一大堆
開發成本激增
....
開發體驗非常糟
Two Questions
怎麼開發 AMP 才不會這麼痛苦?
AMP 有未來嗎?
怎麼開發 AMP 才不會這麼痛苦
Next.js
11ty
wordpress
怎麼開發 AMP 才不會這麼痛苦
拿 amp-script 去搭其他前端框架!
![](https://s3.amazonaws.com/media-p.slid.es/uploads/427531/images/7860308/pasted-from-clipboard.png)
eg.
AMP 有未來嗎?
做 AMP 網站的困境
- 我要的功能沒有相對應的 component
- 但 amp-bind 讓我的 html code 好混亂
- 我很難在 AMP 裡面用 React、Vue 等框架
- 就是那一兩個地方沒辦法過 validation
AMP 有未來嗎?
![](https://s3.amazonaws.com/media-p.slid.es/uploads/427531/images/7853263/pasted-from-clipboard.png)
AMP 全家桶
AMP 有未來嗎?
![](https://s3.amazonaws.com/media-p.slid.es/uploads/427531/images/7857046/Bento_001.jpg)
Bento AMP
AMP 有未來嗎?
Bento AMP
![](https://s3.amazonaws.com/media-p.slid.es/uploads/427531/images/7857046/Bento_001.jpg)
AMP
Non-AMP
AMP
Non-AMP
AMP 有未來嗎?
![](https://s3.amazonaws.com/media-p.slid.es/uploads/427531/images/7857072/螢幕快照_2020-10-23_上午11.42.36.png)
AMP Fest 2020 - What's Next In AMP?
AMP 有未來嗎?
Bento AMP → Valid AMP → Cached AMP
Thanks for listening (ゝ∀・)⌒☆
![](https://media4.giphy.com/media/W1emeLXnYsX8TDmhS5/giphy.gif)
想在 AMP 網頁上做華麗麗效果的我是否搞錯了什麼
By Meng-Ying Tsai
想在 AMP 網頁上做華麗麗效果的我是否搞錯了什麼
- 877