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