Cloudflare WorkersでJavaScriptモジュールのサポートを開始します。JavaScriptで記述されたWorkerの例を見れば、過去数年間にインターネット上で出現するようになった次のコードスニペットに気付くかもしれません:
上記の構文は「Service Worker」APIとして知られており、Webブラウザで使用するために標準化することが提案されました。JavaScriptファイルをWebページにアタッチして、そのHTTP要求と応答を変更し、仮想エンドポイントのように動作させることができます。それはまさにWorkersにとって必要なものであり、 fetch()
や caches
などの標準的なWeb APIともうまく統合されていました。
addEventListener("fetch", (event) => {
event.respondWith(new Response("Hello Worker!"));
}
モジュールを導入する前に、サービスWorker APIが引き続きサポートされることを明確にしておきましょう。 「APIまたは機能が非推奨になっているため、_あなたの_書いたコードを_あなた自身で_書き直す必要がります」というメールを受け取りたりたい開発者は居ません。あなたは私たちからそれを知らされることはありません。私たちがこの決定を下した理由を知りたい場合は、Workersに向けた下位互換性への取り組みについてご確認ください。
JavaScriptモジュールとは
ECMAScript(略称「ES」)モジュールとも呼ばれるJavaScriptモジュールは、JavaScriptでコードをインポートおよびエクスポートするための標準APIです。これは、JavaScriptの「ES6」言語仕様によって導入され、ほとんどのWebブラウザ、Node.js、Deno、そして現在はCloudflare Workersによって実装されています。以下は、これがどのように機能するかを示す例です:
「export」キーワードは、「getDate」関数を現在のモジュールからエクスポートすることを示します。次に、別のモジュールから「import」を使用して、その機能を使用できます。
// filename: ./src/util.js
export function getDate(time) {
return new Date(time).toISOString().split("T")[0]; // "YYYY-MM-DD"
}
以上は基本ですが、モジュールでできることは他にもたくさんあります。これにより、格調高い方法でコードの整理、保守、再利用を、簡単に行うことができます。ここでは、モジュールの_すべて_ の側面について説明することはできませんが、詳細については、モジュールに関する MDNガイド またはLin Clarkによる技術的な詳細をご覧になることをお勧めします。
// filename: ./src/index.js
import { getDate } from "./util.js"
console.log("Today’s date:", getDate());
Workersでモジュールを使用するにはどうすればよいですか?
Workerを表すデフォルトのモジュールをエクスポートできます。「addEventListener」を使用する代わりに、各イベントハンドラーはそのモジュールの関数として定義されます。現在、cronトリガーでは、HTTPおよびWebSocketリクエストの「fetch」と、「scheduled」をサポートしています。
また、各イベントハンドラーのパラメーターなど、他のいくつかの違いにも気付くかもしれません。単一の「イベント」オブジェクトの代わりに、必要とされるパラメータ群はそれぞれ分散されています。最初のパラメータはイベントタイプに固有です。「fetch」の場合は リクエスト オブジェクトであり、「scheduled」の場合はcronスケジュールを含む コントーラー です。
export default {
async fetch(request, environment, context) {
return new Response("I’m a module!");
},
async scheduled(controller, environment, context) {
// await doATask();
}
}
2番目のパラメータは、環境変数を含むオブジェクトです(「バインディング」とも呼ばれます)。以前は、各変数はWorkerのグローバルスコープに挿入されていました。単純なソリューションではありますが、コードに変数を魔法のように表示させるのは混乱を招きます。これで、環境オブジェクトを使用して、環境変数にアクセスするモジュールとライブラリを制御できます。このメカニズムは、欠陥のある、または不要な挙動をするサードパーティライブラリがすべての変数または機密を列挙するのを防止することができるため、より安全です。
3番目のパラメータはコンテキストオブジェクトであり、 waitUntil()
を使用してバックグラウンドタスクを登録できます。これは、イベントの実行をブロックしてはならないロギングやエラーレポートなどのタスクに役立ちます。
これらをすべてまとめると、複数のモジュールをインポートおよびエクスポートしたり、新しいイベントハンドラー構文を使用したりできます。
今週初めに一般提供された、クラスもエクスポートできるDurable Objectsをお忘れなく!Durable Objectクラスを定義する方法です。これは増加された値を返す「Counter」Durable Objectの別の例です。
// filename: ./src/error.js
export async function logError(url, error) {
await fetch(url, {
method: "POST",
body: error.stack
})
}
// filename: ./src/worker.js
import { logError } from "./error.js"
export default {
async fetch(request, environment, context) {
try {
return await fetch(request);
} catch (error) {
context.waitUntil(logError(environment.ERROR_URL, error));
return new Response("Oops!", { status: 500 });
}
}
}
JavaScript以外のモジュールはありますか?
// filename: ./src/counter.js
export class Counter {
value = 0;
fetch() {
this.value++;
return new Response(this.value.toString());
}
}
// filename: ./src/worker.js
// We need to re-export the Durable Object class in the Worker module.
export { Counter } from "./counter.js"
export default {
async fetch(request, environment) {
const clientId = request.headers.get("cf-connecting-ip");
const counterId = environment.Counter.idFromName(clientId);
// Each IP address gets its own Counter.
const counter = environment.Counter.get(counterId);
return counter.fetch("https://counter.object/increment");
}
}
はい!モジュールは主にJavaScript用ですが、他のモジュールタイプもサポートしています。ただし、一部はまだ標準化されていません。
たとえば、 WebAssemblyをモジュールとしてインポートできます。以前、サービスWorker APIには、WebAssemblyがバインディングとして含まれていました。 WebAssemblyは外部リソースではなくコードとして表現する必要があるため、これは間違いだったと思います。モジュールを使用して、WebAssemblyをインポートする新しい方法は次のとおりです:
現在はサポートされていませんが、この 提案で概説されているように、将来はWebAssemblyモジュールとJavaScriptモジュールをより緊密に統合できることを楽しみにしています。以下に示す人間工学的な改善は、WebAssemblyをJavaScriptエコシステムにさらに組み込むために大いに役立つ可能性があります。
import module from "./lib/hello.wasm"
export default {
async fetch(request) {
const instance = await WebAssembly.instantiate(module);
const result = instance.exports.hello();
return new Response(result);
}
}
テキストモジュールとバイナリモジュールのサポートも追加しました。これにより、Stringと ArrayBufferをインポートできるようになりました。標準化されていませんが、HTMLファイルや画像などのリソースを簡単にインポートできるようになりました。
import { hello } from "./lib/hello.wasm"
export default {
async fetch(request) {
return new Response(hello());
}
}
開始方法
<!-- filename: ./public/index.html -->
<!DOCTYPE html>
<html><body>
<p>Hello!</p>
</body></html>
import html from "../public/index.html"
export default {
fetch(request) {
if (request.url.endsWith("/index.html") {
return new Response(html, {
headers: { "Content-Type": "text/html" }
});
}
return fetch(request);
}
}
モジュールを使い始めるには多くの方法があります。
まず、 プレイグラウンド (アカウントは不要)またはダッシュボードの クイックエディタを使用して、ブラウザでモジュールを試すことができます。ブラウザは、モジュールを使用していることを自動的に検出しサービスWorker APIからシームレスに切り替えることができます。現在のところ、ブラウザで作成できるJavaScriptモジュールは1つだけですが、間もなく複数のモジュールの作成が可能になる予定です。
冒険心があり、モジュールを使用して_新しい_ プロジェクトを始めてみたい方は、Workers向けの次世代コマンドラインインターフェイス(CLI)であるwrangler 2.0のベータリリースをお試しいただけます。
既存のプロジェクトでは、wrangler 1.0 (リリース1.17以降)を使用することをお勧めします。モジュールを有効にするには、「wrangler.toml」構成を次の例に合わせます:
モジュールの詳細を説明するためにドキュメントを更新しました。ただし、両方の形式を紹介するように移行するため、一部では引き続きサービスWorker APIを使用します。(おまけとしてTypeScriptもあります!)
name = "my-worker"
type = "javascript"
workers_dev = true
[build.upload]
format = "modules"
dir = "./src"
main = "./worker.js" # becomes "./src/worker.js"
[[build.upload.rules]]
type = "ESModule"
globs = ["**/*.js"]
# Uncomment if you have a build script.
# [build]
# command = "npm run build"
モジュールで何か問題が発生したり、お気づきの点がある場合は、私たちにお知らせください。確認させていただきます。コーディングをお楽しみいただき、あなたがモジュールを使用して作成されるものを楽しみにしています!