###### tags: `PWA` # [Service workers](https://web.dev/learn/pwa/service-workers/) 暫存 Service workers are a fundamental part of a PWA. They enable fast loading (regardless of the network), offline access, push notifications, and other capabilities. Service Worker 是 PWA 的基本組成部分。它們支持快速加載(無論網絡如何)、離線訪問、推送通知和其他功能。 Users expect apps to start on slow or flaky network connections, or even when offline. They expect the content they've most recently interacted with, such as media tracks or tickets and itineraries, to be available and usable. When a request isn't possible, they expect the app to tell them instead of silently failing or crashing. And users wish to do it all quickly. As we can see in this study [Milliseconds make millions](https://web.dev/milliseconds-make-millions/), even a 0.1 second improvement in load times can improve conversion by up to 10%. In summary: users expect PWAs to be reliable and that's why we have service workers. 用戶希望應用程序在網絡連接緩慢或不穩定時啟動,甚至在離線時啟動。他們希望他們最近接觸過的內容(例如媒體曲目或門票和行程)可用且可用。當請求不可行時,他們希望應用程序告訴他們,而不是默默地失敗或崩潰。用戶希望快速完成這一切。正如我們在這項研究中看到的那樣,以毫秒為單位,即使加載時間縮短 0.1 秒,也可以將轉化率提高多達 10%。總結:用戶希望 PWA 可靠,這就是我們擁有 Service Worker 的原因。 ## Hello service workers ![](https://i.imgur.com/ha8rt8q.png) ![](https://i.imgur.com/dAhDa74.png) When an app requests a resource covered by the service worker's scope, including when a user is offline, the service worker intercepts the request, acting as a network proxy. It can then decide if it should serve the resource from the cache via the Cache Storage API, from the network as normally would happen without a service worker, or create it from a local algorithm. This lets you provide a similar experience to that provided by a platform app. It can even work entirely off line. 當應用程序請求 Service Worker 範圍內的資源時,包括用戶離線時,Service Worker 會攔截請求,充當網絡代理。然後它可以決定是否應該通過 Cache Storage API 從緩存中提供資源,或者像通常沒有 service worker 的情況下那樣從網絡中提供資源,還是通過本地算法創建資源。這使您可以提供與平台應用程序類似的體驗。它甚至可以完全脫機工作。 <div style="padding:1rem;background-color:#faf6fe;color:#6001ff;"> <span style="display:flex"> :bulb: <p style="color:#6001ff;margin-top:-1.5em"> Gotchas陷阱 </p> </span> <p style="color:#6001ff;"> Not all browsers support service workers. Even when present your service worker won't be available on first load or while it's waiting to activate. Therefore, treat it as optional and do not require it for core functionality. </p> </div> <div style="padding:1rem;background-color:#faf6fe;color:#6001ff;margin-top:1rem"> <span style="display:flex"> :bulb: <p style="color:#6001ff;margin-top:-1.5em"> Gotchas陷阱 </p> </span> <p style="color:#6001ff;"> 並非所有瀏覽器都支持服務工作者。即使存在,您的 service worker 也不會在首次加載或等待激活時可用。因此,將其視為可選的,核心功能不需要它。 </p> </div> ## Registering a service worker 註冊service worker Before a service worker takes control of your page, it must be registered for your PWA. That means the first time a user comes to your PWA, network requests will go directly to your server because the service worker is not yet in control of your pages. 在服務人員控制您的頁面之前,必須為您的 PWA 註冊它。這意味著當用戶第一次訪問您的 PWA 時,網絡請求將直接發送到您的服務器,因為 service worker 尚未控制您的頁面。 After checking if the browser supports the Service Worker API, your PWA can register a service worker. When loaded, the service worker sets up shop between your PWA and the network, intercepting requests and serving the corresponding responses. 在檢查瀏覽器是否支持 Service Worker API 後,您的 PWA 可以註冊一個 Service Worker。加載後,service worker 在您的 PWA 和網絡之間建立商店,攔截請求並提供相應的響應。 ``` if ('serviceWorker' in navigator) { navigator.serviceWorker.register("/serviceworker.js"); } ``` [範例](https://glitch.com/~learn-pwa-sw-registration) <div style="background-color:#deeafd;padding:1rem;line-height:1.9;margin-top:1rem;color:#174ea6"> There is only one service worker per PWA, but that doesn't mean you need to place the code only in one file. A service worker can include other files using <kbd> importScripts </kbd> in every browser or using [ECMAScript module imports](https://web.dev/es-modules-in-sw/) in some modern browsers. </div> <div style="background-color:#deeafd;padding:1rem;line-height:1.9;margin-top:1rem;color:#174ea6"> 每個 PWA 只有一個服務工作者,但這並不意味著您只需將代碼放在一個文件中。 Service Worker 可以在每個瀏覽器中使用 <kbd> importScripts </kbd>或在某些現代瀏覽器中使用 ECMAScript 模塊導入來包含其他文件。 </div> ## Verify if a service worker is registered驗證服務工作者是否已註冊 To verify if a service worker is registered, use developer tools in your favorite browser. 要驗證服務工作者是否已註冊,請在您喜歡的瀏覽器中使用開發人員工具。 In Firefox and Chromium-based browsers (Microsoft Edge, Google Chrome, or Samsung Internet): 在基於 Firefox 和 Chromium 的瀏覽器(Microsoft Edge、Google Chrome 或 Samsung Internet)中: 1. Open developer tools, then click the **Application** tab. 1. In the left pane, select **Service Workers**. 1. Check that the service worker's script URL appears with the status "Activated". (You'll learn what this status means in the lifecycle section in this chapter). On Firefox the status can be "Running" or "Stopped". <!-- --> 1. 打開開發人員工具,然後單擊“應用程序”選項卡。 2. 在左窗格中,選擇 Service Workers。 3. 檢查 service worker 的腳本 URL 是否顯示為“已激活”狀態。 (您將在本章的生命週期部分了解此狀態的含義)。在 Firefox 上,狀態可以是“正在運行”或“已停止”。 In Safari: 1. Click the Develop menu then the Service Workers submenu. 1. Check that an entry with the current origin appears in the submenu. It opens an inspector over the service worker's context. <!-- --> 1. 單擊 Develop 菜單,然後單擊 Service Workers 子菜單。 2. 檢查當前原點的條目是否出現在子菜單中。它在 service worker 的上下文中打開一個檢查器。 ![](https://i.imgur.com/skk97FQ.png) *Service worker developer tools on Chrome, Firefox and Safari.* Chrome、Firefox 和 Safari 上的服務工作者開發工具。 <div style="background-color:#deeafd;padding:1rem;line-height:1.9;margin-top:1rem;color:#174ea6"> You can remotely inspect a phone or tablet from your desktop browser when you want to check service worker registration status over a mobile device. You will see details on how to do that in the [Tools and Debug chapter](https://web.dev/learn/pwa/tools-and-debug/) </div> <div style="background-color:#deeafd;padding:1rem;line-height:1.9;margin-top:1rem;color:#174ea6"> 當您想通過移動設備檢查 Service Worker 註冊狀態時,您可以從桌面瀏覽器遠程檢查手機或平板電腦。您將在“工具和調試”一章中看到有關如何執行此操作的詳細信息 </div> ## Scope 範圍 The folder your service worker sits in determines its scope. A service worker that lives at <kbd> example.com/my-pwa/sw.js </kbd>can control any navigation at the my-pwa path or below, such as <kbd>example.com/my-pwa/demos/ </kbd>. Service workers can only control items (pages, workers, collectively "clients") in their scope. Scope applies to browser tabs and PWA windows. 您的 Service Worker 所在的文件夾決定了它的範圍。位於 <kbd> example.com/my-pwa/sw.js </kbd> 的服務工作者可以控制 my-pwa 路徑或以下路徑的任何導航,例如 <kbd>example.com/my-pwa/demos/ </kbd>。服務工作者只能控制其範圍內的項目(頁面、工作者,統稱為“客戶端”)。範圍適用於瀏覽器選項卡和 PWA 窗口。 Only one service worker per scope is allowed. When active and running, only one instance is typically available no matter how many clients are in memory (such as PWA windows or browser tabs). 每個範圍只允許一個服務工作者。當處於活動和運行狀態時,通常只有一個實例可用,無論內存中有多少客戶端(例如 PWA 窗口或瀏覽器選項卡)。 <div style="padding:1rem;background-color:#fff5e3;margin-bottom:1em"> <div style="display:flex"> :warning: <p style="color:#c34900;margin-top:-1.5em"> Caution </p> </div> <p style="color:#c34900;"> You should set the scope of your service worker as close to the root of your app as possible. This is the most common setup as it lets the service worker intercept all the requests related to your PWA. Don't put it inside, for instance, a JavaScript folder or have it loaded from a CDN. </p> </div> <div style="padding:1rem;background-color:#fff5e3;margin-bottom:1.5em"> <div style="display:flex"> :warning: <p style="color:#c34900;margin-top:-1.5em"> Caution </p> </div> <p style="color:#c34900;"> 您應該將 Service Worker 的範圍設置為盡可能靠近應用程序的根目錄。這是最常見的設置,因為它可以讓 service worker 攔截與您的 PWA 相關的所有請求。不要將它放在 JavaScript 文件夾中,也不要從 CDN 加載它。 </p> </div> Safari has more complex scope management, known as partitions, affecting how scopes work if you have cross-domain iframes. To read more about WebKit's implementation, read [their blog post](https://webkit.org/blog/8090/workers-at-your-service/). Safari 有更複雜的範圍管理,稱為分區,如果您有跨域 iframe,它會影響範圍的工作方式。要閱讀有關 WebKit 實現的更多信息,請閱讀他們的博客文章。 ## Lifecycle 生命週期 Service workers have a lifecycle that dictates how they are installed, this is separate from your PWA installation. The service worker lifecycle starts with registering the service worker. The browser then attempts to download and parse the service worker file. If parsing succeeds, its install event is fired. The install event only fires once. Service Worker 有一個生命週期來指示它們的安裝方式,這與您的 PWA 安裝是分開的。 Service Worker 生命週期從註冊 Service Worker 開始。然後瀏覽器嘗試下載並解析服務工作者文件。如果解析成功,則觸發其安裝事件。安裝事件只觸發一次。 Service worker installation happens silently, without requiring user permission, even if the user doesn't install the PWA. The Service Worker API is even available on platforms that do not support PWA installation, such as Safari and Firefox on desktop devices. Service Worker 安裝是靜默進行的,不需要用戶許可,即使用戶沒有安裝 PWA。 Service Worker API 甚至可以在不支持 PWA 安裝的平台上使用,例如桌面設備上的 Safari 和 Firefox。 <div style="padding:1rem;background-color:#faf6fe;color:#6001ff;"> <span style="display:flex"> :bulb: <p style="color:#6001ff;margin-top:-1.5em"> Gotchas陷阱 </p> </span> <p style="color:#6001ff;"> Service worker registration and installation, while related, are different events. Registration happens when a page requests a service worker by calling register() as described previously. Installation happens when a registered service worker exists, can be parsed as JavaScript, and doesn't throw any errors during its first execution. </p> </div> <div style="padding:1rem;background-color:#faf6fe;color:#6001ff;margin-top:1rem"> <span style="display:flex"> :bulb: <p style="color:#6001ff;margin-top:-1.5em"> Gotchas陷阱 </p> </span> <p style="color:#6001ff;"> Service Worker 註冊和安裝雖然相關,但卻是不同的事件。如前所述,當頁面通過調用 register() 請求 service worker 時,就會發生註冊。安裝發生在註冊的 service worker 存在時,可以被解析為 JavaScript,並且在第一次執行時不會拋出任何錯誤。 </p> </div> After the installation, the service worker is not yet in control of its clients, including your PWA. It needs to be activated first. When the service worker is ready to control its clients, the <kbd> activate </kbd> event will fire. This doesn't mean, though, that the page that registered the service worker will be managed. By default, the service worker will not take control until the next time you navigate to that page, either due to reloading the page or re-opening the PWA. 安裝後,Service Worker 還無法控制其客戶端,包括您的 PWA。它需要先被激活。當 service worker 準備好控制其客戶端時,將觸發 activate 事件。但這並不意味著註冊 service worker 的頁面將被管理。默認情況下,Service Worker 在您下次導航到該頁面之前不會取得控制權,這可能是由於重新加載頁面或重新打開 PWA。 You can listen for events in the service worker's global scope using the <kbd>self </kbd> object. 您可以使用 self 對像在 service worker 的全局範圍內監聽事件。 serviceworker.js ``` // This code executes in its own worker or thread self.addEventListener("install", event => { console.log("Service worker installed"); }); self.addEventListener("activate", event => { console.log("Service worker activated"); }); ``` ## Updating a service worker / 更新service worker Service workers get updated when the browser detects that the service worker currently controlling the client and the new (from your server) version of the same file are byte-different. 當瀏覽器檢測到當前控制客戶端的服務工作人員和同一文件的新(來自您的服務器)版本字節不同時,服務工作人員會更新。 <div style="padding:1rem;background-color:#fff5e3;margin-bottom:1.5em"> <div style="display:flex"> :warning: <p style="color:#c34900;margin-top:-1.5em"> Caution </p> </div> <p style="color:#c34900;"> When updating your service worker, do so without renaming it. Do not even add file hashes to the filename. Otherwise, the browser will never get the new version of your service worker! </p> </div> <div style="padding:1rem;background-color:#fff5e3;margin-bottom:1em"> <div style="display:flex"> :warning: <p style="color:#c34900;margin-top:-1.5em"> Caution </p> </div> <p style="color:#c34900;"> 更新 Service Worker 時,不要重命名。甚至不要將文件哈希添加到文件名。否則,瀏覽器將永遠無法獲取您的 service worker 的新版本! </p> </div> After a successful installation, the new service worker will wait to activate until the existing (old) service worker no longer controls any clients. This state is called "waiting", and it's how the browser ensures that only one version of your service worker is running at a time. Refreshing a page or reopening the PWA won't make the new service worker take control. The user needs to close or navigate away from all tabs and windows using the current service worker and then navigate back. Only then will the new service worker to take control. Visit this [service worker lifecycle article](https://web.dev/service-worker-lifecycle/) for more information. 成功安裝後,新的 service worker 將等待激活,直到現有(舊)service worker 不再控制任何客戶端。這種狀態稱為“等待”,瀏覽器就是這樣確保一次只有一個版本的 service worker 在運行。刷新頁面或重新打開 PWA 不會讓新的 Service Worker 接管控制權。用戶需要使用當前服務工作者關閉或導航離開所有選項卡和窗口,然後導航回來。只有這樣,新的 service worker 才會接管控制權。訪問此服務工作者生命週期文章以獲取更多信息。 ## Service worker lifespan / Service worker壽命 Once installed and registered, a service worker can manage all network requests within its scope. It runs on its own thread, with activation and termination controlled by the browser. This lets it work even before or after your PWA is open. While service workers run on their own thread, there is no guarantee that in-memory state will persist between runs of a service worker, so make sure anything you want to reuse on each run is available either in IndexedDB or some other persistent storage. 一旦安裝並註冊,service worker 就可以管理其範圍內的所有網絡請求。它在自己的線程上運行,激活和終止由瀏覽器控制。這讓它甚至可以在您的 PWA 打開之前或之後工作。雖然服務工作者在自己的線程上運行,但無法保證內存中的狀態在服務工作者的運行之間持續存在,因此請確保您希望在每次運行時重用的任何內容在 IndexedDB 或其他一些持久性存儲中可用。 If not already running, a service worker will start whenever a network request in its scope is asked for, or when a triggering event, like periodic background sync or a push message, is received. Service workers don't live indefinitely. While exact timings differ between browsers, service workers will be terminated if they've been idle for a few seconds, or if they've been busy for too long. If a service worker has been terminated and an event occurs that would start it up, it will restart. ## Capabilities With a registered and active service worker, you have a thread with a completely different execution lifecycle than the main one on your PWA. However, by default, the service worker file itself has no behavior. It won't cache or serve any resources, as this has to be done by your code. You'll find out how in the following chapters. 使用已註冊且活躍的 Service Worker,您的線程的執行生命週期與 PWA 上的主線程完全不同。但是,默認情況下,service worker 文件本身沒有任何行為。它不會緩存或提供任何資源,因為這必須由您的代碼完成。您將在接下來的章節中了解具體方法。 Service worker's capabilities are not just for proxy or serving HTTP requests; other features are available on top of it for other purposes, such as background code execution, web push notifications, and process payments. We'll discuss these additions in the [capabilities chapter](https://web.dev/learn/pwa/capabilities/). Service Worker 的功能不僅僅用於代理或服務 HTTP 請求;其他功能可用於其他目的,例如後台代碼執行、網絡推送通知和處理付款。我們將在功能章節中討論這些新增功能。 ## Resources * [Service Worker API (MDN)](https://developer.mozilla.org/zh-CN/docs/Web/API/Service_Worker_API) * [Service Worker mindset](https://web.dev/service-worker-mindset/) * [WebKit Workers at your service](https://webkit.org/blog/8090/workers-at-your-service/) * [ES Modules in Service Workers](https://web.dev/es-modules-in-sw/) * [Service worker lifecycle](https://web.dev/service-worker-lifecycle/)