想在 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> 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>
亮紅燈
哪泥!又亮!
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?
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?
什麼時候會想用到 iframe?
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 (ゝ∀・)⌒☆
想在 AMP 網頁上做華麗麗效果的我是否搞錯了什麼
By Meng-Ying Tsai
想在 AMP 網頁上做華麗麗效果的我是否搞錯了什麼
- 1,006