會 動 的
一年 365 天歡迎餵食,請多指教 ヽ(●´∀`●)ノ
做個網站,什麼都好但就是沒有流量
嘗試來做全站 AMP 化
超絕快速的網頁效能
Bing / Google 會幫你 serve cache 頁面!
框架限制大、導入成本高
在 Critical Rendering Path 上做各種優化
限制東限制西
<!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>
<!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 ⚡
<!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>
<!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 嗎?
<!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
使用 validation 工具 📖
Browser Developer Console
https://你的網址#development=1
Browser Developer Console
Browser Extension
invalid
valid
cached
Web Interface
Web Interface
VS Code extension
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);
}
});
Command Line Tool
npm install -g amphtml-validator
amphtml-validator https://amp.dev/
#=> https://amp.dev/: PASS
不會怎樣
沒有 Google / Bing Cache
沒有對應的 search result
Google Search Console 會叫
<!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
AMP 要求你必須使用 component 「amp-img」
它會幫你:
<amp-img layout='fixed'
width='800'
height='600'
src="https://source.unsplash.com/kAiyiesI_Kk/800x600"
alt="a lovely fox"
/>
跟平常的 <img>比起來多了 layout 的部分
為了守護世界的正義避免 content reflow
以及讓 asset 還沒載回來就可以 render layout
幾乎所有的 AMP component 都需要指定 layout
fixed
intrinsic
responsive
fill
fixed-height
fiex-item
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>
開開心心 🎶
<link href=".../style.css" rel="stylesheet">
.m-auto {
margin: auto;
}
/* ... */
咦咦咦咦?我又做錯什麼了嗎?!
AMP 希望一個 request 裡就可以獲得所有 CSS 規則
只允許一個 <style> tag 和 inline CSS
<style amp-custom>
.m-auto {
margin: auto;
}
/* ... */
</style>
amp-custom 要記得加哦
好歐
<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>
哪泥!又亮!
平常會用 JS 做的事情必須被 AMP 元件所取代
除非大量使用 amp-script ,
要不然你可能很久都不會碰到 javascript
廣告 → amp-ad
圖片輪播 → amp-carousel
那如果沒有適合我的 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 版
(心)
<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),
擅長透過改變 state 來製造
充滿 interaction 的 AMP 網頁。
記得 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
[text]
[src]
[disabled]
[class]
[width]
[height]
[hidden]
[aria-label]
👉 Binding Types 👈
在可以跟不可以之間
你可以先定義起來
但只有在 setState 發生後才會有效
<amp-state id="info">
<script type="application/json">
"按下按鈕會看到這行"
</script>
</amp-state>
<div [text]="info">還沒按按鈕看到的是這行</div>
<button on="tap:AMP.setState({})">Set state!</button>
JSON 資料的來源也可以是某個 remote endpint
<amp-state id="info" src="https://my-site.blah/items.json">
</amp-state>
有owo/
<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>
來看看官方範例
我們有兩個範例 endpoint
GET /favorite:
回傳是否被加入我的最愛,true 代表有加入,false 代表沒加入
POST /favorite:
對他送 true / false 可以改變「我的最愛」的狀態
當按鈕被按就送 xhr request 出去(X)
放個 form,把愛心鈕設計成 submit 按鈕(O)
先放個 AMP state
<amp-state id="favorite"
credentials="include"
src=".../favorite">
</amp-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>
先準備一個 form
當按下 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>
你說:我不記得 <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>
和普通的吸塵器 (form) 很像
只是多了一些功能跟特色。
<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 網頁就會失去活力。
還記得 Google / Bing cache 嗎?
為了確保你的頁面上的資訊是新的,
必須用 amp-list 來撈資料
www.google.com/amp/s/mysite.blah
你想買的辣個酷東西
現在只要 $81000
緊張!存貨只剩 666
mysite.blah
你想買的商品
已經下架了呦 O.<
click 買!
買!
「庫存不是還有 666 嗎?」
買不到酷東西的民眾會森七七
記得 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-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
<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>
<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>
class="{{#.}}heart-fill{{/.}}{{^.}}heart-border{{/.}}"
[class]="favorite ? 'heart-fill' : 'heart-border'"
/items/1/favorite 回傳內容
false
amp-list 根據從 endpoint 拿回來的值決定 class 是 heart-fill 或 heart-border
class="{{#.}}heart-fill{{/.}}{{^.}}heart-border{{/.}}"
[class]="favorite ? 'heart-fill' : 'heart-border'"
AMP.printState()
true
當 "favorite" 這個 AMP state 被改變時,根據它的值決定 class 是 heart-fill 還是 heart-border
<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>
雖然都參雜在 html 裡真的頗醜w
AMP 沒有提供
專門讓你直接操作 localStorage 的元件
你可能會想用 localStorage 處理的東西
你可能會想用 localStorage 處理的東西
amp-user-notification
amp-script
amp-consent
amp-consent:
amp-user-notification
require script
<script async custom-element="amp-consent" src="https://cdn.ampproject.org/v0/amp-consent-0.1.js"></script>
<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>
consent configuration
{
"consentInstanceId": :"consent-id",
"consentRequired": true,
"promptUI": "consentDialog",
"postPromptUI": "post-consent-ui"
}
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>
饒了我吧...我已經掰不出來了 _(´ཀ`」 ∠)_
喜歡散步玩耍的丁丁,
透過允許部分 javascript 來增添
amp 網頁的豐富與變化
require script
<script async custom-element="amp-script" src="https://cdn.ampproject.org/v0/amp-script-0.1.js"></script>
外部 script
<amp-script layout="container"
src=".../hello-world.js"
>
<button id="hello-url">Say hello!</button>
</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 ❌
requires user gestures to change page content.
<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>
<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>
amp-script or amp-iframe
amp-facebook
amp-instagram
amp-twitter
有人可以幫我想一下介紹詞嗎 QQ
毫無反應,就是隻迪西
<!-- 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>
tap: AMP.setState({myState: blah.map({}).join('....'),
anotherState.reduce({...})})
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-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
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 😃
amp-bind 複雜度限制
amp-bind 的 JSON 資料大小限制
有在做 AMP 網頁?
聽完了以後覺得不想做 AMP?
我討厭 AMP
相當好的頁面效能
豐富的 component 可以使用
AMP Cache!
難客製化
難維護
限制一大堆
開發成本激增
....
開發體驗非常糟
怎麼開發 AMP 才不會這麼痛苦?
AMP 有未來嗎?
Next.js
11ty
wordpress
拿 amp-script 去搭其他前端框架!
eg.
做 AMP 網站的困境
AMP 全家桶
Bento AMP
Bento AMP
AMP
Non-AMP
AMP
Non-AMP
AMP Fest 2020 - What's Next In AMP?
Bento AMP → Valid AMP → Cached AMP