Edited at

AMP で任意の JS を実行できる amp-script を試してみた

明日の AMP Conf のための事前に動かしてみた。


amp-script とは何か

今まで https://github.com/ampproject/amphtml に登録された一部のJSしか出来なかったAMPだが、amp-script を使うと任意の JS を実行できる。ただし、 WebWorker コンテキストの中でのみ。

本来ならDOMが存在しないWebWorkerだが、しかしDOMに触れないわけではない。 WebWorker の中に DOM と似たようなオブジェクトが実装されており、それを操作することでメインスレッドのJSに反映される。

この DOM がすごい2018: worker-dom - mizchi's blog

https://github.com/ampproject/worker-dom


amp-script を導入する


static/index.html

  <head>

<!-- ... -->
<script async src="https://cdn.ampproject.org/v0.js"></script>
<script
async
custom-element="amp-script"
src="https://cdn.ampproject.org/v0/amp-script-0.1.js"
></script>
</head>
<body>
<amp-script layout="container" src="/main.js">
<button id="hello">Insert Hello World!</button>
</amp-script>
</body>


static/main.js

const button = document.getElementById("hello");

button.addEventListener("click", () => {
const el = document.createElement("h1");
el.textContent = "Hello World!";
document.body.appendChild(el);
});

このコードは実際には main-thread で実行されておらず web-worker での実行されている。

ここで具体的にどういうコードを書けるかは https://github.com/ampproject/worker-dom の demo を参照

この例は単純すぎるDOM操作だが、 preact も動くとのこと。 React が動くは不明だが、worker-dom のソースコードを読んだ限り考慮されてはいる。


AMP用 CORS サーバーを立てる

このままではまだ動かない。このコードを AMP として動かすには、AMP用に動くことを証明する Header を付与する必要がある。

CORS in AMP - amp.dev を参考に、雑に node サーバーを立てた。


server.js

const express = require("express");

const app = express();

app.set("port", process.env.PORT || 3000);

app.use((req, res, next) => {
const host = req.get("host");
const protocol = host.startsWith("localhost") ? "http" : "https";
const origin = protocol + "://" + req.get("host");
res.set("Access-Control-Allow-Origin", origin);
res.set("AMP-Access-Control-Allow-Source-Origin", origin);
res.set(
"Access-Control-Allow-Headers",
"Origin, X-Requested-With, Content-Type, Accept"
);
res.set("Access-Control-Allow-Methods", "GET, POST, OPTIONS, HEAD, PUT");
res.set("Access-Control-Allow-Credentials", "true");
next();
});

app.use(express.static(__dirname + "/static"));

app.listen(app.get("port"), function() {
console.log("Listening on port " + app.get("port"));
});



package.json

{

"name": "tryamp",
"version": "1.0.0",
"main": "index.js",
"author": "mizchi <miz404@gmail.com>",
"license": "MIT",
"scripts": {
"now-start": "node server.js"
},
"dependencies": {
"express": "^4.16.4"
}
}

(npm install などは略)

こういう構成を意図してる。

server.js

package.json
static/
hello.js
index.html

ローカルで動くのもなんなので now.sh にデプロイ

npm i -g now

now deploy

https://tryamp-ywasobhymt.now.sh/ にデプロイした。


開発者フラグを立てる

(この節は将来的に不要)

まだ動かない。 https://tryamp-ywasobhymt.now.sh/ (または localhost のサーバー) で DevTools を開いて AMP.toggleExperiment('amp-script') を eval する。この状態でリロードするとやっと有効になる


おわり

次は preact + typescript やる