INTEGRITY Dokumentace

Migrace ze Service Workers na ES Modules

Tento průvodce vám ukáže, jak migrovat vaše Workers z Service Worker formát do ES moduly formát.

Výhody migrace

Existuje několik důvodů, proč migrovat vaše Workers na formát ES modulů:

  1. Váš Worker poběží rychleji. U service workers jsou bindings vystaveny jako globální proměnné. To znamená, že runtime Workers musí pro každý požadavek vytvořit nový spouštěcí kontext JavaScriptu, což přidává režii a čas navíc. Workery napsané pomocí ES modulů mohou stejný spouštěcí kontext znovu využít napříč více požadavky.
  2. Implementace Durable Objects vyžaduje Workers, které používají ES moduly.
  3. Bindings pro D1, Workers AI, Vectorize, Workflows, a Obrázky lze použít pouze z Workerů, které využívají ES modules.
  4. Můžete postupně nasazovat změny do svého Workeru při použití formátu ES modulů.
  5. Workery využívající ES moduly můžete snadno publikovat do npm, což vám umožní importovat a znovu používat Workers ve vaší kódové základně.

Migrace Workeru

Následující příklad ukazuje Worker, který přesměruje všechny příchozí požadavky na adresu URL s 301 stavový kód.

Se syntaxí Service Worker vypadá ukázkový Worker takto:

async function handler(request) {
  const base = 'https://example.com';
  const statusCode = 301;

  const destination = new URL(request.url, base);
  return Response.redirect(destination.toString(), statusCode);
}

// Initialize Worker
addEventListener('fetch', event => {
  event.respondWith(handler(event.request));
});

Workers ve formátu ES modules nahrazují addEventListener syntax s definicí objektu, která musí být výchozím exportem souboru (přes export default). Předchozí ukázkový kód se změní na:

export default {
  fetch(request) {
    const base = "https://example.com";
    const statusCode = 301;

    const source = new URL(request.url);
    const destination = new URL(source.pathname, base);
    return Response.redirect(destination.toString(), statusCode);
  },
};

Bindings

Bindings umožňují vašim Workers interagovat se zdroji na Cloudflare developer platform.

Workers ve formátu ES modules se nespoléhají na žádné globální bindings. Syntaxe Service Worker naopak přistupuje k bindings v globálním rozsahu.

Chcete-li porozumět bindings, přečtěte si následující TODO příklad KV namespace bindingu. Pro vytvoření TODO KV namespace binding provedete následující:

  1. Vytvořte KV namespace s názvem My Tasks a získáte ID, které použijete ve svém bindingu.
  2. Vytvořte Worker.
  3. Najděte svého Workeru Konfigurační soubor Wrangler a přidejte binding KV namespace:
{
	"kv_namespaces": [
		{
			"binding": "TODO",
			"id": "<ID>"
		}
	]
}
[[kv_namespaces]]
binding = "TODO"
id = "<ID>"

V následujících částech použijete svůj binding ve formátu Service Worker i ES modules.

Bindings ve formátu Service Worker

V syntaxi Service Worker váš TODO KV namespace binding je definován v globálním rozsahu vašeho Workeru. Váš TODO KV namespace binding lze použít kdekoli v kódu vaší aplikace Worker.

addEventListener("fetch", async (event) => {
  return await getTodos()
});

async function getTodos() {
  // Get the value for the "to-do:123" key
  // NOTE: Relies on the TODO KV binding that maps to the "My Tasks" namespace.
  let value = await TODO.get("to-do:123");

  // Return the value, as is, for the Response
  event.respondWith(new Response(value));
}

Bindings ve formátu ES modulů

Ve formátu ES modulů jsou bindings dostupné pouze uvnitř env parametru poskytnutého ve vstupním bodu vašeho Workeru.

Chcete-li získat přístup k TODO KV namespace binding ve vašem kódu Workeru, env parametr musí být předán z fetch handler ve vašem Workeru na getTodos .

import { getTodos } from './todos'

export default {
  async fetch(request, env, ctx) {
    // Passing the env parameter so other functions
    // can reference the bindings available in the Workers application
    return await getTodos(env)
  },
};

Následující kód představuje getTodos funkce, která volá get funkce na TODO KV binding.

async function getTodos(env) {
  // NOTE: Relies on the TODO KV binding which has been provided inside of
  // the env parameter of the `getTodos` function
  let value = await env.TODO.get("to-do:123");
  return new Response(value);
}

export { getTodos }

Proměnné prostředí

Proměnné prostředí se v kódu psaném ve formátu ES modules přistupuje jinak než ve formátu Service Worker.

Prohlédněte si následující ukázkovou konfiguraci proměnné prostředí v Konfigurační soubor Wrangler:

{
	"$schema": "./node_modules/wrangler/config-schema.json",
	"name": "my-worker-dev",
	// Define top-level environment variables
	// using the {"vars": "key": "value"} format
	"vars": {
		"API_ACCOUNT_ID": "<EXAMPLE-ACCOUNT-ID>"
	}
}
"$schema" = "./node_modules/wrangler/config-schema.json"
name = "my-worker-dev"

[vars]
API_ACCOUNT_ID = "<EXAMPLE-ACCOUNT-ID>"

Proměnné prostředí ve formátu Service Worker

Ve formátu Service Worker API_ACCOUNT_ID je definována v globálním rozsahu vaší aplikace Worker. Váš API_ACCOUNT_ID proměnná prostředí je k dispozici kdekoli v kódu vaší aplikace Worker.

addEventListener("fetch", async (event) => {
  console.log(API_ACCOUNT_ID) // Logs "<EXAMPLE-ACCOUNT-ID>"
  return new Response("Hello, world!")
})

Proměnné prostředí ve formátu ES modulů

Ve formátu ES modulů jsou proměnné prostředí dostupné prostřednictvím env parametru poskytnutého ve vstupním bodu vaší aplikace Worker:

export default {
  async fetch(request, env, ctx) {
    console.log(env.API_ACCOUNT_ID) // Logs "<EXAMPLE-ACCOUNT-ID>"
    return new Response("Hello, world!")
  },
};

Můžete také importovat env z cloudflare:workers pro přístup k proměnným prostředí odkudkoli ve vašem kódu, včetně nejvyššího rozsahu (top-level scope):

import { env } from "cloudflare:workers";

// Access environment variables at the top level
const accountId = env.API_ACCOUNT_ID;

export default {
	async fetch(request) {
		console.log(accountId); // Logs "<EXAMPLE-ACCOUNT-ID>"
		return new Response("Hello, world!");
	},
};
import { env } from "cloudflare:workers";

// Access environment variables at the top level
const accountId = env.API_ACCOUNT_ID;

export default {
  async fetch(request: Request): Promise<Response> {
    console.log(accountId) // Logs "<EXAMPLE-ACCOUNT-ID>"
    return new Response("Hello, world!")
  },
};

Tento přístup je užitečný pro inicializaci konfigurace nebo přístup k proměnným prostředí z hluboce vnořených funkcí, aniž byste je museli předávat env při každém volání funkce. Další podrobnosti najdete v Import env jako globální.

Cron Triggers

Chcete-li zpracovat Cron Trigger událost ve Workeru napsaném v syntaxi ES modulů, implementujte scheduled() obslužná rutina události, což je ekvivalent naslouchání na scheduled událost v syntaxi Service Worker.

Tento ukázkový kód:

addEventListener("scheduled", (event) => {
  // ...
});

Poté se stane:

export default {
  async scheduled(event, env, ctx) {
    // ...
  },
};

Přístup event nebo context data

Workers často potřebují přístup k datům, která nejsou v request objekt. Workers například někdy používají waitUntil pro zpoždění spuštění. Workery používající formát ES modulů mají přístup k waitUntil prostřednictvím context parametru. Viz parametry ES modulů kde najdete další informace.

Tento ukázkový kód:

async function triggerEvent(event) {
  // Fetch some data
  console.log('cron processed', event.scheduledTime);
}

// Initialize Worker
addEventListener('scheduled', event => {
  event.waitUntil(triggerEvent(event));
});

Poté se stane:

async function triggerEvent(event) {
  // Fetch some data
  console.log('cron processed', event.scheduledTime);
}

export default {
  async scheduled(event, env, ctx) {
    ctx.waitUntil(triggerEvent(event));
  },
};

Syntaxe Service Worker

Worker napsaný v syntaxi Service Worker se skládá ze dvou částí:

  1. Event listener, který naslouchá FetchEvents.
  2. Obslužná rutina události, která vrací Odpověď objekt, který se předává do vlastnosti události .respondWith() metoda.

Když některý z globálních síťových serverů Cloudflare přijme požadavek na URL odpovídající Workeru, server Cloudflare předá požadavek runtime prostředí Workers. To odešle FetchEvent v izolát kde Worker běží.

addEventListener('fetch', event => {
  event.respondWith(handleRequest(event.request));
});

async function handleRequest(request) {
  return new Response('Hello worker!', {
    headers: { 'content-type': 'text/plain' },
  });
}

Níže je příklad pracovního postupu zpracování požadavku a odpovědi:

  1. Event listener pro FetchEvent říká skriptu, aby naslouchal všem požadavkům přicházejícím do vašeho Workeru. Event handleru se předává event objekt, který zahrnuje event.request, Request objekt, což je reprezentace HTTP požadavku, který spustil FetchEvent.

  2. Volání .respondWith() umožňuje běhovému prostředí Workers zachytit požadavek a vrátit vlastní odpověď (v tomto příkladu prostý text 'Hello worker!').

    • FetchEvent handler obvykle vyvrcholí voláním metody .respondWith() buď s Response nebo Promise<Response> která určuje odpověď.

    • FetchEvent objekt také poskytuje dvě další metody ke zpracování neočekávaných výjimek a operací, které se mohou dokončit až po vrácení odpovědi.

Další informace o metody životního cyklu fetch() handler.

Podporováno FetchEvent vlastnosti

respondWith

Zachytí požadavek a umožní Workeru odeslat vlastní odpověď.

Pokud fetch obslužná rutina události nevolá respondWith, runtime doručí událost dalšímu registrovanému fetch obslužná rutina události. Jinými slovy, i když se to nedoporučuje, je možné přidat více fetch obslužné rutiny událostí ve Workeru.

Pokud žádný fetch obslužná rutina události volá respondWith, pak runtime přesměruje požadavek na origin, jako by Worker neexistoval. Pokud ale origin neexistuje, nebo pokud je vaším origin serverem samotný Worker, což platí vždy pro *.workers.dev domén, pak musíte volat respondWith pro platnou odpověď.

// Format: Service Worker
addEventListener('fetch', event => {
  let { pathname } = new URL(event.request.url);

  // Allow "/ignore/*" URLs to hit origin
  if (pathname.startsWith('/ignore/')) return;

  // Otherwise, respond with something
  event.respondWith(handler(event));
});

waitUntil

waitUntil příkaz prodlužuje životnost "fetch" událost. Přijímá Promise-ovou úlohu, kterou runtime Workers spustí předtím, než handler skončí, ale bez blokování odpovědi. To je ideální například pro cachování odpovědí nebo zpracování logování.

Ve formátu Service Worker waitUntil je dostupná v rámci event protože se jedná o nativní FetchEvent vlastnost.

Ve formátu ES modules waitUntil je přesunuto a dostupné v context objekt parametrů.

// Format: Service Worker
addEventListener('fetch', event => {
  event.respondWith(handler(event));
});

async function handler(event) {
  // Forward / Proxy original request
  let res = await fetch(event.request);

  // Add custom header(s)
  res = new Response(res.body, res);
  res.headers.set('x-foo', 'bar');

  // Cache the response
  // NOTE: Does NOT block / wait
  event.waitUntil(caches.default.put(event.request, res.clone()));

  // Done
  return res;
}

passThroughOnException

passThroughOnException metoda zabraňuje chybové odpovědi za běhu, když Worker vyvolá neošetřenou výjimku. Skript místo toho fail open, který přesměruje požadavek na origin server, jako kdyby Worker vůbec nebyl vyvolán.

Aby chyby JavaScriptu nezpůsobily selhání celých požadavků kvůli nezachyceným výjimkám, passThroughOnException() způsobí, že Workers runtime předá řízení origin serveru.

Ve formátu Service Worker passThroughOnException se přidá do FetchEvent rozhraní, čímž je zpřístupní v rámci event.

Ve formátu ES modules passThroughOnException je dostupná na context objekt parametrů.

// Format: Service Worker
addEventListener('fetch', event => {
  // Proxy to origin on unhandled/uncaught exceptions
  event.passThroughOnException();
  throw new Error('Oops');
});