Skip to main content

服务工作进程

介绍

🌐 Introduction

warning

服务工作者仅在基于 Chromium 的浏览器上受支持。

🌐 Service workers are only supported on Chromium-based browsers.

note

如果你想进行一般的网络模拟、路由和拦截,请先查看网络指南。Playwright 为此用例提供了内置 API,不需要以下信息。然而,如果你对 Service Worker 自身发出的请求感兴趣,请继续阅读以下内容。

Service Workers 提供了一种浏览器原生方法,用于处理页面通过原生 Fetch API (fetch) 发出的请求,以及其他网络请求的资源(如脚本、CSS 和图片)。

它们可以作为页面与外部网络之间的网络代理来执行缓存逻辑,或者如果 Service Worker 添加了 FetchEvent 监听器,则可以为用户提供离线体验。

🌐 They can act as a network proxy between the page and the external network to perform caching logic or can provide users with an offline experience if the Service Worker adds a FetchEvent listener.

许多使用 Service Workers 的网站只是将它们作为一种透明的优化技术。虽然用户可能会注意到体验更快,但应用的实现并不意识到它们的存在。无论是否启用 Service Workers,运行应用在功能上看起来是相同的。

🌐 Many sites that use Service Workers simply use them as a transparent optimization technique. While users might notice a faster experience, the app's implementation is unaware of their existence. Running the app with or without Service Workers enabled appears functionally equivalent.

如何禁用服务工作者

🌐 How to Disable Service Workers

Playwright 允许在测试期间禁用服务工作者。这使得测试更加可预测和高效。但是,如果你的实际页面使用了服务工作者,行为可能会有所不同。

🌐 Playwright allows to disable Service Workers during testing. This makes tests more predictable and performant. However, if your actual page uses a Service Worker, the behavior might be different.

要禁用服务工作者,请将 service_workers 上下文选项设置为 "block"

🌐 To disable service workers, set service_workers context option to "block".

conftest.py
import pytest

@pytest.fixture(scope="session")
def browser_context_args(browser_context_args):
return {
**browser_context_args,
"service_workers": "block"
}

访问 Service Worker 并等待激活

🌐 Accessing Service Workers and Waiting for Activation

你可以使用 browser_context.service_workers 来列出 Service Worker,或者如果你预期某个页面会触发其 注册,可以专门监视该 Service Worker

🌐 You can use browser_context.service_workers to list the Service Workers, or specifically watch for the Service Worker if you anticipate a page will trigger its registration:

with context.expect_event("serviceworker") as worker_info:
page.goto("/example-with-a-service-worker.html")
service_worker = worker_info.value

browser_context.on("serviceworker") 事件在 Service Worker 接管页面之前触发,因此在使用 worker.evaluate() 在 worker 中执行之前,你应该等待它的激活。

还有更惯用的等待 Service Worker 被激活的方法,但以下是与实现无关的方法:

🌐 There are more idiomatic methods of waiting for a Service Worker to be activated, but the following is an implementation agnostic method:

page.evaluate("""async () => {
const registration = await window.navigator.serviceWorker.getRegistration();
if (registration.active?.state === 'activated')
return;
await new Promise(resolve => {
window.navigator.serviceWorker.addEventListener('controllerchange', resolve);
});
}""")

网络事件和路由

🌐 Network Events and Routing

Service Worker 提出的任何网络请求都通过 BrowserContext 对象报告:

🌐 Any network request made by the Service Worker is reported through the BrowserContext object:

此外,对于 页面 发起的任何网络请求,当请求由 Service Worker 的 fetch 处理程序处理时,方法 response.from_service_worker 会返回 true

🌐 Additionally, for any network request made by the Page, method response.from_service_worker return true when the request was handled a Service Worker's fetch handler.

考虑一个简单的服务工作进程,它会捕获页面发出的每个请求:

🌐 Consider a simple service worker that fetches every request made by the page:

如果 index.html 注册了这个服务工作者,然后再获取 data.json,将会触发以下请求/响应事件(以及相应的网络生命周期事件):

🌐 If index.html registers this service worker, and then fetches data.json, the following Request/Response events would be emitted (along with the corresponding network lifecycle events):

EventOwnerURLRoutedresponse.from_service_worker
browser_context.on("request")Frameindex.htmlYes
page.on("request")Frameindex.htmlYes
browser_context.on("request")Service Workertransparent-service-worker.jsYes
browser_context.on("request")Service Workerdata.jsonYes
browser_context.on("request")Framedata.jsonYes
page.on("request")Framedata.jsonYes

由于示例 Service Worker 只是作为一个基本的透明“代理”

🌐 Since the example Service Worker just acts a basic transparent "proxy":

  • data.json 有两个 browser_context.on("request") 事件:一个由 Frame 拥有,另一个由 Service Worker 拥有。
  • 只有由服务工作进程(Service Worker)拥有的资源请求可以通过 browser_context.route() 路由;由 Frame 拥有的 data.json 事件无法路由,因为它们根本不可能访问外部网络,因为服务工作进程已经注册了 fetch 处理程序。
caution

需要注意的是:如果在具有非空 request.service_workerRequest/Response 上调用 request.frameresponse.frame,将会 抛出 异常。

🌐 It's important to note: calling request.frame or response.frame will throw an exception, if called on a Request/Response that has a non-null request.service_worker.

仅路由服务工作进程请求

🌐 Routing Service Worker Requests Only

def handle_route(route: Route):
if route.request.service_worker:
# NB: accessing route.request.frame here would THROW
route.fulfill(content_type="text/plain", status=200, body="from sw")
else:
route.continue_()

context.route("**", handle_route)

已知限制

🌐 Known Limitations

目前无法路由对更新的 Service Worker 主脚本代码的请求 (https://github.com/microsoft/playwright/issues/14711)。

🌐 Requests for updated Service Worker main script code currently cannot be routed (https://github.com/microsoft/playwright/issues/14711).