Workbox-Window v4.x 中文版
Workbox 目前發了一個大版本,從 v3.x 到了 v4.x,變化有挺大的,下面是在 window 環境下的模塊。
什么是 workbox-window?
workbox-window 包是一組模塊,用于在 window 上下文中運行,也就是說,在你的網頁內部運行。 它們是 servicewoker 中運行的其他 workbox 的補充。
workbox-window的主要功能/目標是:
- 通過幫助開發人員確定 serviceWorker 生命周期中最關鍵的時刻,并簡化對這些時刻的響應,簡化 serviceWoker 注冊和更新的過程。
- 幫助防止開發人員犯下最常見的錯誤。
- 使 serviceWorker 程序中運行的代碼與 window 中運行的代碼之間的通信更加輕松。
導入和使用 workbox-window
workbox-window 包的主要入口點是 Workbox 類,你可以從我們的CDN或使用任何流行的 JavaScript 打包工具將其導入代碼中。
使用我們的 CDN
在您的網站上導入 Workbox 類的最簡單方法是從我們的 CDN:
<script type="module"> import {Workbox} from 'https://storage.googleapis.com/workbox-cdn/releases/4.0.0/workbox-window.prod.mjs';if ('serviceWorker' in navigator) {const wb = new Workbox('/sw.js');wb.register(); } </script>注意,此示例使用 <script type ="module"> 和 import 語句來加載 Workbox 類。 雖然您可能認為需要轉換此代碼以使其在舊版瀏覽器中運行,但實際上并不是必需的。
支持 serviceWorker 的所有主要瀏覽器也支持 JavaScript 模塊,因此將此代碼提供給任何瀏覽器都是完美的(舊版瀏覽器將忽略它)。
通過 JavScript 打包加載 Workbox
雖然使用 Workbox 絕對不需要工具,但如果您的開發基礎架構已經包含了與 npm 依賴項一起使用的 webpack 或 Rollup 等打包工具,則可以使用它們來加載 Workbox。
第一步就是安裝 Workbox 做為你應用的依賴:
npm install workbox-window然后,在您的某個應用程序的 JavaScript 文件中,通過引用 workbox-window 包名稱導入 Workbox:
import {Workbox} from 'workbox-window';if ('serviceWorker' in navigator) {const wb = new Workbox('/sw.js');wb.register(); }如果您的打包工具支持通過動態 import 語句進行代碼拆分,你還可以有條件地加載workbox-window,這有助于減少頁面主包的大小。
盡管 Workbox 非常小(1kb gzip壓縮),但是沒有理由需要加載站點的核心應用程序邏輯,因為 serviceWorker 本質上是漸進式增強。
if ('serviceWorker' in navigator) {const {Workbox} = await import('workbox-window');const wb = new Workbox('/sw.js');wb.register(); }高級打包概念
與在 Service worker 中運行的 Workbox 包不同,workbox-window 在 package.json 中的 main 和 module 字段引用的構建文件被轉換為 ES5。 這使它們與當今的構建工具兼容 - 其中一些不允許開發人員轉換其 node_module 依賴項的任何內容。
如果你的構建系統允許您轉換依賴項(或者如果您不需要轉換任何代碼),那么最好導入特定的源文件而不是包本身。
以下是你可以導入 Workbox 的各種方法,以及每個方法將返回的內容的說明:
// 使用ES5語法導入UMD版本 // (pkg.main: "build/workbox-window.prod.umd.js") const {Workbox} = require('workbox-window');// 使用ES5語法導入模塊版本 // (pkg.module: "build/workbox-window.prod.es5.mjs") import {Workbox} from 'workbox-window';// 使用ES2015 +語法導入模塊源文件 import {Workbox} from 'workbox-window/Workbox.mjs';重要! 如果您直接導入源文件,則還需要配置構建過程以縮小文件,并在將其部署到生產時刪除僅開發代碼。 有關詳細信息,請參閱使用打包(webpack / Rollup)和Workbox的指南。
示例
導入 Workbox 類后,可以使用它來注冊 serviceWorker 并與之交互。 以下是您可以在應用程序中使用 Workbox 的一些示例:
注冊 serviceWorker 并在 serviceWorker 第一次處于 active 狀態時通知用戶:
許多 Web 應用程序用戶 serviceWorker 預緩存資源,以便其應用程序在后續頁面加載時離線工作。在某些情況下,通知用戶該應用程序現在可以離線使用是有意義的。
const wb = new Workbox('/sw.js');wb.addEventListener('activated', (event) => {// 如果另一個版本的 serviceWorker,`event.isUpdate`將為true// 當這個版本注冊時,worker 正在控制頁面。if (!event.isUpdate) {console.log('Service worker 第一次激活!');// 如果您的 serviceWorker 配置為預緩存資源,那么// 資源現在都應該可用。} });// 添加事件偵聽器后注冊 serviceWorker 。 wb.register();如果 serviceWorker 已安裝但等待激活,則通知用戶
當由現有 serviceWorker 控制的頁面注冊新的 serviceWorker 時,默認情況下,在初始 serviceWorker 控制的所有客戶端完全卸載之前,serviceWorker 將不會激活。
這是開發人員常見的混淆源,特別是在重新加載當前頁面不會導致新 serviceWorker 程序激活的情況下。
為了幫助減少混淆并在發生這種情況時明確說明,Workbox 類提供了一個可以監聽的等待事件:
const wb = new Workbox('/sw.js');wb.addEventListener('waiting', (event) => {console.log(`已安裝新的 serviceWorker,但無法激活` +`直到運行當前版本的所有選項卡都已完全卸載。`); });// 添加事件偵聽器后注冊 service worker 。 wb.register();從 workbox-broadcast-update 包通知用戶緩存更新
workbox-broadcast-update 包非常棒
能夠從緩存中提供內容(快速交付)的方式,同時還能夠通知用戶該內容的更新(使用stale-while-revalidate 策略)。
要從 window 接收這些更新,您可以偵聽 CACHE_UPDATE 類型的消息事件:
const wb = new Workbox('/sw.js');wb.addEventListener('message', (event) => {if (event.data.type === 'CACHE_UPDATE') {const {updatedURL} = event.data.payload;console.log(`${updatedURL} 的更新版本可用`);} });// 添加事件偵聽器后注冊 service worker。 wb.register();向 serviceWorker 發送要緩存的URL列表
對于某些應用程序,可以知道在構建時需要預先緩存的所有資源,但某些應用程序根據用戶首先登陸的 URL 提供完全不同的頁面。
對于后一類別的應用程序,僅緩存用戶所訪問的特定頁面所需的資源可能是有意義的。 使用 workbox-routing 軟件包時,您可以向路由器發送一個 URL 列表進行緩存,它將根據路由器本身定義的規則緩存這些 URL。
每當激活新的 serviceWorker 時,此示例都會將頁面加載的 URL 列表發送到路由器。 請注意,發送所有 URL 是可以的,因為只會緩存與 serviceWorker 中定義的路由匹配的 URL:
const wb = new Workbox('/sw.js');wb.addEventListener('activated', (event) => {// 獲取當前頁面URL +頁面加載的所有資源。const urlsToCache = [location.href,...performance.getEntriesByType('resource').map((r) => r.name),];// 將該URL列表發送到 serviceWorker 的路由器。wb.messageSW({type: 'CACHE_URLS',payload: {urlsToCache},}); });// 添加事件偵聽器后注冊 serviceWorker。 wb.register();注意:上述技術適用于通過默認路由器上的 workbox.routing.registerRoute() 方法定義的任何路由。 如果您要創建自己的路由器實例,則需要手動調用 addCacheListener() 。
重要的 serviceWorker 生命周期
serviceWorker 的生命周期很復雜,完全可以理解。 它之所以如此復雜,部分原因在于它必須處理 serviceWorker 所有可能使用的所有邊緣情況(例如,注冊多個 serviceWorker,在不同的框架中注冊不同的 serviceWorker,注冊具有不同名稱的 serviceWorker 等)。
但是大多數實現 serviceWorker 的開發人員不應該擔心所有這些邊緣情況,因為它們的使用非常簡單。 大多數開發人員每頁加載只注冊一個 serviceWorker,并且他們不會更改他們部署到服務器的 serviceWorker 文件的名稱。
Workbox 類通過將所有 serviceWorker 注冊分為兩類來包含 serviceWorker 生命周期的這個更簡單的視圖:實例自己的注冊 serviceWorker 和外部 serviceWorker:
- 注冊 serviceWorker:由于 Workbox 實例調用 register() 而已開始安裝的 serviceWorker,或者如果調用 register() 未在注冊時觸發 updatefound 事件,則已啟用安裝 serviceWorker。
- 外部 serviceWorker:一個 serviceWorker,開始獨立于 Workbox 實例調用 register() 安裝。 當用戶在另一個標簽頁中打開新版本的網站時,通常會發生這種情況。
我們的想法是,來自 serviceWorker 的所有生命周期事件都是你的代碼應該期待的事件,而來自外部 serviceWorker 的所有生命周期事件都應該被視為具有潛在危險,并且應該相應地警告用戶。
考慮到這兩類 serviceWorker,下面是所有重要serviceWorker 生命周期時刻的細分,以及開發人員如何處理它們的建議:
第一次安裝 serviceWorker
你可能希望在 serviceWorker 第一次安裝時不同于處理所有未來更新的方式。
在 Workbox 中,你可以通過檢查以下任何事件的 isUpdate 屬性來區分版本首次安裝和未來更新。 對于第一次安裝,isUpdate 將為 false。
const wb = new Workbox('/sw.js');wb.addEventListener('installed', (event) => {if (!event.isUpdate) {// 在這里編寫第一次安裝需要的代碼} });wb.register();| 新的 serviceWorker 已安裝(第一次) | installed | serviceWorker 第一次安裝時,通常會預先緩存網站離線工作所需的所有資源。 你可以考慮通知用戶他們的站點現在可以離線運行。 此外,由于 serviceWorker 第一次安裝它時不會截獲該頁面加載的獲取事件,你也可以考慮緩存已加載的資源(盡管如果這些資源已經被預先緩存,則不需要這樣做)。 向上面的緩存示例發送 serviceWorker 的URL列表顯示了如何執行此操作。 |
| serviceWorker 已經控制頁面 | controlling | 安裝新 serviceWorker 程序并開始控制頁面后,所有后續獲取事件都將通過該 serviceWorker 程序。 如果你的 serviceWorker 添加了任何特殊邏輯來處理特定的 fetch 事件,那么當你知道邏輯將運行時就是這一點。 請注意,第一次安裝 serviceWorker 時,它不會開始控制當前頁面,除非該 serviceWorker 在其 activate 事件中調用 clients.claim()。 默認行為是等到下一頁加載開始控制。 從 workbox-window 的角度來看,這意味著僅在 serviceWorker 調用 clients.claim() 的情況下才調度 controlling 事件。 如果在注冊之前已經控制了頁面,則不會調度此事件。 |
| serviceWorker 已經完成激活 | activated | 如上所述,serviceWorker 第一次完成激活它可能(或可能不)已經開始控制頁面。 因此,你不應該將 activate 事件視為了解 serviceWorker 何時控制頁面的方式。 但是,如果你在活動事件中(在 serviceWorker )運行邏輯,并且你需要知道該邏輯何時完成,則激活的事件將讓你知道。 |
發現 serviceWorker 的更新版本時
當新 serviceWorker 開始安裝但現有版本當前正在控制該頁面時,以下所有事件的 isUpdate 屬性都將為 true。
在這種情況下,你的反應通常與第一次安裝不同,因為你必須管理用戶何時以及如何獲得此更新。
| 已安裝新 serviceWorker(更新前一個) | installed | 如果這不是第一個 serviceWorker 安裝(event.isUpdate === true),則表示已找到并安裝了較新版本的 serviceWorker(即,與當前控制頁面的版本不同)。 這通常意味著已將更新版本的站點部署到你的服務器,并且新資源可能剛剛完成預先緩存。 注意:某些開發人員使用已安裝的事件來通知用戶其新版本的站點可用。 但是,根據我是否在安裝 serviceWorker 程序中調用 skipWaiting(),安裝的 serviceWorker 可能會立即生效,也可能不會立即生效。 如果你確實調用 skipWaiting(),那么最好在新 serviceWorker 激活后通知用戶更新,如果你沒有調用 skipWaiting,最好通知他們等待事件中的掛起更新(見下文了解更多信息) 細節)。 |
| serviceWorker 已安裝,但它仍處于等待階段 | waiting | 如果 serviceWorker 的更新版本在安裝時未調用skipWaiting(),則在當前活動 serviceWorker 控制的所有頁面都已卸載之前,它不會激活。 你可能希望通知用戶更新可用,并將在下次訪問時應用。 警告! 開發人員通常會提示用戶重新加載以獲取更新,但在許多情況下刷新頁面不會激活已安裝的工作程序。 如果用戶刷新頁面并且serviceWorker 仍在等待,則等待事件將再次觸發,并且 event.wasWaitingBeforeRegister 屬性將為 true。 請注意,我們計劃在將來的版本中改進此體驗。 關注問題#1848以獲取更新。 另一種選擇是提示用戶并詢問他們是否想要獲得更新或繼續等待。 如果選擇獲取更新,則可以使用 postMessage() 告訴 serviceWorker 運行 skipWaiting()。 有關示例,請參閱高級配方為用戶提供頁面重新加載。 |
| serviceWorker 已開始控制頁面 | controlling | 當更新的 serviceWorker 開始控制頁面時,這意味著當前控制的 serviceWorker 的版本與加載頁面時控制的版本不同。 在某些情況下可能沒問題,但也可能意味著當前頁面引用的某些資源不再位于緩存中(也可能不在服務器上)。 你可能需要考慮通知用戶頁面的某些部分可能無法正常工作。 注意:如果不在serviceWorker 中調用 skipWaiting(),則不會觸發控制事件。 |
| serviceWorker 已完成激活 | activated | 當更新的 serviceWorker 完成激活時,這意味著你在 serviceWorker 的激活中運行的任何邏輯都已完成。 如果有什么需要延遲,直到邏輯完成,這是運行它的時間。 |
找到意外版本的 serviceWorker
有時用戶會在很長一段時間內在后臺標簽中打開你的網站。 他們甚至可能會打開一個新標簽并導航到你的網站,卻沒有意識到他們已經在后臺標簽中打開了您的網站。 在這種情況下,您的網站可能同時運行兩個版本,這可能會為開發人員帶來一些有趣的問題。
考慮這樣一種情況,即您的網站的標簽 A 正在運行 v1,標簽 B 正在運行 v2。 加載選項卡 B 時,它將由 v1 附帶的 serviceWorker 版本控制,但服務器返回的頁面(如果使用網絡優先緩存策略用于導航請求)將包含所有 v2 資源。
這對于選項卡 B 來說通常不是問題,因為當你編寫 v2 代碼時,你知道你的 v1 代碼是如何工作的。但是,它可能是標簽A的問題,因為你的 v1 代碼無法預測你的 v2 代碼可能會引入哪些更改。
為了幫助處理這些情況,workbox-window 還會在檢測到來自“外部” serviceWorker 的更新時調度生命周期事件,其中 external 表示任何不是當前 Workbox 實例注冊的版本。
| 已安裝外部 serviceWorker | externalinstalled | 如果已安裝外部 serviceWorker,則可能意味著用戶在不同的選項卡中運行你網站的較新版本。 如何響應可能取決于已安裝的服務是進入等待還是活動階段。 |
| 通過等待激活來安裝外部 serviceWorker | externalwaiting | 如果外部 serviceWorker 正在等待激活,則可能意味著用戶試圖在另一個選項卡中獲取你網站的新版本,但是由于此選項卡仍處于打開狀態,因此他們已被阻止。 如果發生這種情況,你可以考慮向用戶顯示通知,要求他們關閉此標簽。 在極端情況下,你甚至可以考慮調用 window.reload(),如果這樣做不會導致用戶丟失任何已保存的狀態。 |
| serviceWorker 外部 serviceWorker 已激活 | externalactivated | 如果外部 serviceWorker 程序已激活,則當前頁面很可能無法繼續正常運行。 你可能需要考慮向用戶顯示他們正在運行舊版本頁面的通知,并且可能會出現問題。 |
避免常見錯誤
Workbox 提供的最有用的功能之一是它的開發人員日志記錄。 對于 worbox-window 也是這樣。
我們知道與 serviceWorker 一起開發往往會讓人感到困惑,當事情發生與你期望的相反時,很難知道原因。
例如,當你對 serviceWorker 進行更改并重新加載頁面時,你可能無法在瀏覽器中看到該更改。 最可能的原因是,你的 serviceWorker 仍在等待激活。
但是當使用 Workbox 類注冊 serviceWorker 時,你將被告知開發人員控制臺中的所有生命周期狀態更改,這應該有助于調試為什么事情不像你期望的那樣。
此外,開發人員在首次使用 serviceWorker 時常犯的錯誤是在錯誤的范圍內注冊 serviceWorker。
為了防止這種情況發生,Workbox類將警告您注冊服務工作者的頁面是否不在該服務工作者的范圍內。 如果您的服務工作者處于活動狀態但尚未控制該頁面,它還會警告你:
window 到 serviceWorker 的溝通
大多數高級 serviceWorker 使用涉及 serviceWorker 和 window 之間的消息傳遞丟失。 Workbox 類通過提供 messageSW() 方法來幫助解決這個問題,該方法將postMessage() 實例的注冊 serviceWorker 并等待響應。
雖然你可以以任何格式向 serviceWorker 發送數據,但所有 Workbox 包共享的格式是具有三個屬性的對象(后兩個是可選的):
| type | 是 | string | 標識此消息的唯一字符串。 按照慣例,類型都是大寫的,下劃線分隔單詞。 如果類型表示要采取的動作,則它應該是現在時的命令(例如 CACHE_URLS ),如果類型表示報告的信息,則它應該是過去時(例如 URLS_CACHED )。 |
| meta | 否 | string | 在 Workbox 中,這始終是發送消息的 Workbox 包的名稱。 自己發送郵件時,可以省略此屬性或將其設置為你喜歡的任何內容。 |
| payload | 否 | * | 正在發送的數據。 通常這是一個對象,但它不一定是。 |
通過 messageSW() 方法發送的消息使用 MessageChannel,因此接收方可以響應它們。 要響應消息,你可以在消息事件偵聽器中調用 event.ports[0].postMessage(response)。 messageSW() 方法返回一個 promise,該 promise 將解析為你返回的任何響應。
這是一個從 window 到 serviceWorker 發送消息并獲得響應的示例。 第一個代碼塊是 serviceWorker 中的消息偵聽器,第二個塊使用 Workbox 類發送消息并等待響應:
sw.js 中的代碼:
const SW_VERSION = '1.0.0';addEventListener('message', (event) => {if (event.data.type === 'GET_VERSION') {event.ports[0].postMessage(SW_VERSION);} });main.js 中的代碼(運行在 window 環境):
const wb = new Workbox('/sw.js'); wb.register();const swVersion = await wb.messageSW({type: 'GET_VERSION'}); console.log('Service Worker version:', swVersion);管理版本不兼容性
上面的示例顯示了如何從 window 中實現檢查 serviceWorker 版本。 使用此示例是因為當你在 window 和 serviceWorker 之間來回發送消息時,請務必注意你的 serviceWorker 可能沒有運行與你的頁面代碼運行相同的站點版本,以及 處理此問題的解決方案會有所不同,具體取決于你是以網絡優先服務還是緩存優先服務。
網絡優先
首先為你的網頁提供服務時,你的用戶將始終從你的服務器獲取最新版本的 HTML。 但是,當用戶第一次重新訪問你的站點時(在部署更新之后),他們獲得的 HTML 將是最新版本,但在其瀏覽器中運行的 serviceWorker 將是先前安裝的版本(可能是許多舊版本)。
理解這種可能性非常重要,因為如果當前版本的頁面加載的 JavaScript 向舊版本的 serviceWorker 發送消息,則該版本可能不知道如何響應(或者它可能以不兼容的格式響應)。
因此,在進行任何關鍵工作之前,始終對 serviceWorker 進行版本控制并檢查兼容版本是個好主意。
例如,在上面的代碼中,如果該 messageSW() 調用返回的 serviceWorker 版本早于預期版本,則最好等到找到更新(這應該在調用 register() 時發生)。 此時,你可以通知用戶或更新,也可以手動跳過等待階段以立即激活新的 serviceWorker。
緩存優先
與在網絡服務頁面時相比,首先,當你首先提供頁面緩存時,你知道你的頁面最初將始終與 serviceWorker 的版本相同(因為這是服務它的原因)。 因此,立即使用messageSW() 是安全的。
但是,如果找到 serviceWorker 的更新版本并在頁面調用 register() 時激活(即你有意跳過等待階段),則向其發送消息可能不再安全。
管理這種可能性的一種策略是使用版本控制方案,允許你區分中斷更新和非中斷更新,并且在更新中斷的情況下,你知道向 serviceWorker 發送消息是不安全的。 相反,你需要警告用戶他們正在運行舊版本的頁面,并建議他們重新加載以獲取更新。
博客名稱:王樂平博客
CSDN博客地址:http://blog.csdn.net/lecepin
本作品采用知識共享署名-非商業性使用-禁止演繹 4.0 國際許可協議進行許可。總結
以上是生活随笔為你收集整理的Workbox-Window v4.x 中文版的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: linux操作系统cp命令
- 下一篇: 微信怎么at所有人_微信怎么艾特所有人