← Cloudflare Workers / workers / reference
Переход с Service Workers на ES Modules
В этом руководстве вы узнаете, как перенести Workers с Service Worker ↗ формат в ES modules ↗ формате.
Преимущества миграции
Есть несколько причин перейти на формат ES modules для ваших Workers:
- Ваш Worker будет работать быстрее. В service worker привязки предоставляются как глобальные переменные. Из-за этого для каждого запроса среда выполнения Workers вынуждена создавать новый контекст выполнения JavaScript, что добавляет накладные расходы и время. Worker, написанные с использованием ES-модулей, могут переиспользовать один и тот же контекст выполнения для нескольких запросов.
- Реализация Durable Objects требует, чтобы Workers использовали формат ES modules.
- Привязки для D1, Workers AI, Vectorize, Workflows, а также Изображения можно использовать только в Workers, использующих ES modules.
- Вы можете постепенно развёртывать изменения в вашем Worker при использовании формата ES modules.
- Workers, использующие модули ES, легко публикуются в
npm, что позволяет импортировать и повторно использовать Workers в вашей кодовой базе.
Миграция Worker
В следующем примере показан Worker, который перенаправляет все входящие запросы на URL с 301 код состояния.
При использовании синтаксиса Service Worker пример Worker выглядит так:
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, использующие формат ES modules, заменяют addEventListener синтаксис с определением объекта, который должен быть экспортом по умолчанию файла (через export default). Предыдущий пример кода принимает вид:
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 позволяют вашим Workers взаимодействовать с ресурсами платформы разработчика Cloudflare.
Workers, использующие формат ES modules, не полагаются на глобальные привязки. Однако синтаксис Service Worker обращается к привязкам через глобальную область видимости.
Чтобы разобраться в привязках, см. следующее TODO Пример привязки пространства имён KV. Чтобы создать TODO привязку пространства имён KV, вы:
- Создайте пространство имён KV с именем
My Tasksи получить ID, который будет использоваться в привязке. - Создайте Worker.
- Найдите у вашего Worker конфигурационный файл Wrangler и добавьте привязку пространства имен KV:
{
"kv_namespaces": [
{
"binding": "TODO",
"id": "<ID>"
}
]
}[[kv_namespaces]]
binding = "TODO"
id = "<ID>"В следующих разделах вы будете использовать привязку в форматах Service Worker и ES modules.
Привязки в формате Service Worker
В синтаксисе Service Worker ваш TODO Привязка пространства имён KV определена в глобальной области видимости вашего Worker. Ваш TODO Привязка пространства имён KV доступна для использования в любом месте кода вашего 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));
}Привязки в формате ES modules
В формате ES modules привязки доступны только внутри env параметр, предоставляемый в точке входа Worker.
Чтобы получить доступ к TODO привязку пространства имён KV в коде вашего Worker, env параметр необходимо передать из fetch обработчик в вашем Worker к 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)
},
};Следующий код представляет собой getTodos функция, которая вызывает get функцию на TODO привязка KV.
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 }Переменные окружения
Переменные окружения доступны по-разному в коде, написанном в формате ES modules, и в формате Service Worker.
Ознакомьтесь со следующим примером настройки переменных окружения в конфигурационный файл 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>"Переменные окружения в формате Service Worker
В формате Service Worker API_ACCOUNT_ID определён в глобальной области видимости вашего приложения Worker. Ваш API_ACCOUNT_ID переменная окружения доступна для использования в любом месте кода вашего приложения Worker.
addEventListener("fetch", async (event) => {
console.log(API_ACCOUNT_ID) // Logs "<EXAMPLE-ACCOUNT-ID>"
return new Response("Hello, world!")
})Переменные окружения в формате ES modules
В формате ES modules переменные окружения доступны через env параметр, предоставляемый в точке входа приложения Worker:
export default {
async fetch(request, env, ctx) {
console.log(env.API_ACCOUNT_ID) // Logs "<EXAMPLE-ACCOUNT-ID>"
return new Response("Hello, world!")
},
};Также можно импортировать env от cloudflare:workers для доступа к переменным окружения из любого места в коде, включая область видимости верхнего уровня:
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!")
},
};Такой подход удобен для инициализации конфигурации или доступа к переменным окружения из глубоко вложенных функций без передачи env через каждый вызов функции. Подробнее см. Импорт env как глобальный.
Cron Triggers
Чтобы обработать Cron Trigger событие в Worker, написанном в синтаксисе модулей ES, реализуйте scheduled() обработчик события, что эквивалентно прослушиванию scheduled событие в синтаксисе Service Worker.
Код этого примера:
addEventListener("scheduled", (event) => {
// ...
});Затем принимает вид:
export default {
async scheduled(event, env, ctx) {
// ...
},
};Доступ event или context данные
Воркерам часто нужен доступ к данным, которых нет в request объект. Например, иногда Workers используют waitUntil чтобы отложить выполнение. Workers, использующие формат ES modules, могут обращаться к waitUntil через context параметр. См. параметры ES modules для получения дополнительной информации.
Код этого примера:
async function triggerEvent(event) {
// Fetch some data
console.log('cron processed', event.scheduledTime);
}
// Initialize Worker
addEventListener('scheduled', event => {
event.waitUntil(triggerEvent(event));
});Затем принимает вид:
async function triggerEvent(event) {
// Fetch some data
console.log('cron processed', event.scheduledTime);
}
export default {
async scheduled(event, env, ctx) {
ctx.waitUntil(triggerEvent(event));
},
};Синтаксис Service Worker
Worker, написанный в синтаксисе Service Worker, состоит из двух частей:
- Обработчик события, отслеживающий
FetchEvents. - Обработчик события, возвращающий Ответ объект, который передаётся в свойство события
.respondWith()метод.
Когда на один из глобальных серверов сети Cloudflare поступает запрос на URL, соответствующий Worker, сервер Cloudflare передает этот запрос в среду выполнения Workers. Это инициирует FetchEvent в изолят где выполняется Worker.
addEventListener('fetch', event => {
event.respondWith(handleRequest(event.request));
});
async function handleRequest(request) {
return new Response('Hello worker!', {
headers: { 'content-type': 'text/plain' },
});
}Ниже приведён пример рабочего процесса запроса и ответа:
-
Обработчик события для
FetchEventуказывает скрипту прослушивать любые запросы, поступающие к вашему Worker. В обработчик события передаетсяeventобъект, который включаетevent.request,Requestобъект, представляющий HTTP запрос, который вызвалFetchEvent. -
Вызов
.respondWith()позволяет среде выполнения Workers перехватывать запрос, чтобы вернуть собственный ответ (в этом примере обычный текст'Hello worker!').-
FetchEventобработчик обычно завершается вызовом метода.respondWith()либо сResponseилиPromise<Response>который определяет ответ. -
FetchEventобъект также предоставляет два других метода для обработки непредвиденных исключений и операций, которые могут завершиться уже после отправки ответа.
-
Подробнее о методы жизненного цикла fetch() обработчик.
Поддерживается FetchEvent свойства
-
event.typeстрока- Тип события. Всегда возвращает
"fetch".
- Тип события. Всегда возвращает
-
event.requestRequest- Входящий HTTP-запрос.
-
event.respondWith(responseResponse|Promise): void- См.
respondWith.
- См.
-
event.waitUntil(promisePromise): void- См.
waitUntil.
- См.
-
event.passThroughOnException(): void
respondWith
Перехватывает запрос и позволяет Worker отправить собственный ответ.
Если fetch обработчик события не вызывает respondWith, среда выполнения доставляет событие следующему зарегистрированному fetch обработчик события. Другими словами, хотя это не рекомендуется, это означает, что можно добавить несколько fetch обработчиков событий в Worker.
Если нет fetch обработчик события вызывает respondWith, тогда среда выполнения перенаправляет запрос на origin, как если бы Worker не сработал. Однако если origin отсутствует, либо сам Worker является вашим origin-сервером, что всегда верно для *.workers.dev доменов, то вам нужно вызвать respondWith для получения корректного ответа.
// 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 команда продлевает время жизни "fetch" событие. Он принимает Promise-ориентированная задача, которую среда выполнения Workers запустит до завершения обработчика, не блокируя при этом ответ. Например, это идеально подходит для кеширование ответов или обработки логирования.
В формате Service Worker waitUntil доступен в event потому что это встроенный FetchEvent свойство.
В формате ES modules waitUntil перемещен и доступен в context объект параметров.
// 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 метод предотвращает ответ с ошибкой времени выполнения, когда Worker выбрасывает необработанное исключение. Вместо этого скрипт fail open ↗, который перенаправит запрос на исходный сервер так, будто Worker вообще не вызывался.
Чтобы ошибки JavaScript из-за необработанных исключений не приводили к сбою всего запроса, passThroughOnException() приводит к тому, что среда выполнения Workers передаёт управление серверу источника.
В формате Service Worker passThroughOnException добавляется в FetchEvent интерфейс, делая его доступным в event.
В формате ES modules passThroughOnException доступен на context объект параметров.
// Format: Service Worker
addEventListener('fetch', event => {
// Proxy to origin on unhandled/uncaught exceptions
event.passThroughOnException();
throw new Error('Oops');
});