W3Cschool
恭喜您成為首批注冊(cè)用戶(hù)
獲得88經(jīng)驗(yàn)值獎(jiǎng)勵(lì)
本頁(yè)講的是如何使用 Angular Service Worker 發(fā)布和支持生產(chǎn)環(huán)境下的應(yīng)用。它解釋了 Angular Service Worker 如何滿(mǎn)足大規(guī)模生產(chǎn)環(huán)境的需求、Service Worker 在多種條件下有哪些行為以及有哪些可用的資源和故障保護(hù)機(jī)制。
從概念上說(shuō),你可以把 Angular Service Worker 想象成一個(gè)轉(zhuǎn)發(fā)式緩存或裝在最終用戶(hù)瀏覽器中的 CDN 邊緣。Service Worker 的工作是從本地緩存中滿(mǎn)足 Angular 應(yīng)用對(duì)資源或數(shù)據(jù)的請(qǐng)求,而不用等待網(wǎng)絡(luò)。和所有緩存一樣,它有一些規(guī)則來(lái)決定內(nèi)容該如何過(guò)期或更新。
在 Angular Service Worker 的語(yǔ)境下,“版本”是指用來(lái)表示 Angular 應(yīng)用的某一次構(gòu)建成果的一組資源。當(dāng)應(yīng)用的一個(gè)新的構(gòu)建發(fā)布時(shí),Service Worker 就把它看做此應(yīng)用的一個(gè)新版本。就算只修改了一個(gè)文件,也同樣如此。在任何一個(gè)給定的時(shí)間,Service Worker 可能會(huì)在它的緩存中擁有此應(yīng)用的多個(gè)版本,這幾個(gè)版本也都能用于提供服務(wù)。
要保持應(yīng)用的整體性,Angular Service Worker 會(huì)用所有的文件共同組成一個(gè)版本。組成版本的這些文件通常包括 HTML、JS 和 CSS 文件。把這些文件分成一組是至關(guān)重要的,因?yàn)樗鼈儠?huì)互相引用,并且依賴(lài)于一些特定內(nèi)容。比如,?index.html
? 文件可能有個(gè)引用 ?bundle.js
? 的 ?<script>
? 標(biāo)簽,它可能會(huì)試圖從這個(gè)腳本中調(diào)用一個(gè) ?startApp()
? 函數(shù)。任何時(shí)候,只要這個(gè)版本的 ?index.html
? 被提供了,與它對(duì)應(yīng)的 ?bundle.js
? 也必須同時(shí)提供。這種情況下,使用調(diào)用了 ?startApp()
? 的老的 ?index.html
? 并同時(shí)使用定義了 ?runApp()
? 的新 bundle 就是無(wú)效的。
當(dāng)使用惰性加載模塊時(shí),文件的整體性就顯得格外重要。某個(gè) JS 包可能引用很多惰性塊,而這些惰性塊的文件名在應(yīng)用的每次特定的構(gòu)建中都是唯一的。如果運(yùn)行應(yīng)用的 ?X
? 版本視圖加載一個(gè)惰性塊,但該塊的服務(wù)器已經(jīng)升級(jí)到了 ?X + 1
? 版本,這次惰性加載操作就會(huì)失敗。
本應(yīng)用的版本標(biāo)識(shí)符由其所有資源的內(nèi)容決定,如果它們中的任何一個(gè)發(fā)生了變化,則版本標(biāo)識(shí)符也隨之改變。實(shí)際上,版本是由 ?ngsw.json
? 文件的內(nèi)容決定的,包含了所有已知內(nèi)容的哈希值。如果任何一個(gè)被緩存的文件發(fā)生了變化,則該文件的哈希也將在 ?ngsw.json
? 中隨之變化,從而導(dǎo)致 Angular Service Worker 將這個(gè)活動(dòng)文件的集合視為一個(gè)新版本。
?
ngsw.json
? 是在構(gòu)建時(shí)基于 ?ngsw-config.json
? 生成的清單文件。
借助 Angular Service Worker 的這種版本控制行為,應(yīng)用服務(wù)器就可以確保這個(gè) Angular 應(yīng)用中的這組文件始終保持一致。
每當(dāng)用戶(hù)打開(kāi)或刷新應(yīng)用程序時(shí),Angular Service Worker 都會(huì)通過(guò)查看清單(manifest)文件 “ngsw.json” 的更新來(lái)檢查該應(yīng)用程序的更新。如果它找到了更新,就會(huì)自動(dòng)下載并緩存這個(gè)版本,并在下次加載應(yīng)用程序時(shí)提供。
長(zhǎng)周期緩存的潛在副作用之一就是可能無(wú)意中緩存了無(wú)效的資源。在普通的 HTTP 緩存中,硬刷新或緩存過(guò)期限制了緩存這種無(wú)效文件導(dǎo)致的負(fù)面影響。而 Service Worker 會(huì)忽略這樣的約束,事實(shí)上會(huì)對(duì)整個(gè)應(yīng)用程序進(jìn)行長(zhǎng)期緩存。因此,讓 Service Worker 獲得正確的內(nèi)容就顯得至關(guān)重要。
為了確保資源的整體性,Angular Service Worker 會(huì)驗(yàn)證所有帶哈希的資源的哈希值。通常,對(duì)于 ?Angular CLI
? 應(yīng)用程序,用戶(hù)的 ?src/ngsw-config.json
? 配置文件中會(huì)涵蓋 ?dist
?目錄下的所有內(nèi)容。
如果某個(gè)特定的文件未能通過(guò)驗(yàn)證,Angular Service Worker 就會(huì)嘗試用 “cache-busting” URL 為參數(shù)重新獲取內(nèi)容,以消除瀏覽器或中間緩存的影響。如果該內(nèi)容也未能通過(guò)驗(yàn)證,則 Service Worker 會(huì)認(rèn)為該應(yīng)用的整個(gè)版本都無(wú)效,并停止用它提供服務(wù)。如有必要,Service Worker 會(huì)進(jìn)入安全模式,這些請(qǐng)求將退化為直接訪(fǎng)問(wèn)網(wǎng)絡(luò)。如果服務(wù)無(wú)效、損壞或內(nèi)容過(guò)期的風(fēng)險(xiǎn)很高,則會(huì)選擇不使用緩存。
導(dǎo)致哈希值不匹配的原因有很多:
ngsw.json
?。反之,也可能發(fā)生沒(méi)有更新資源,卻更新了 ?ngsw.json
? 的情況。?ngsw.json
? 清單中唯一帶哈希值的資源就是構(gòu)建清單時(shí) ?dist
?目錄中的資源。而其它資源,特別是從 CDN 加載的資源,其內(nèi)容在構(gòu)建時(shí)是未知的,或者會(huì)比應(yīng)用程序部署得更頻繁。
如果 Angular Service Worker 沒(méi)有哈希可以驗(yàn)證給定的資源,它仍然會(huì)緩存它的內(nèi)容,但會(huì)使用 “重新驗(yàn)證時(shí)失效” 的策略來(lái)承認(rèn) HTTP 緩存頭。也就是說(shuō),當(dāng)被緩存資源的 HTTP 緩存頭指出該資源已過(guò)期時(shí),Angular Service Worker 將繼續(xù)提供內(nèi)容,并嘗試在后臺(tái)刷新資源。這樣,那些被破壞的非哈希資源留在緩存中的時(shí)間就不會(huì)超出為它配置的生命周期。
如果應(yīng)用程序的資源版本突然發(fā)生了變化或沒(méi)有給出警告,就可能會(huì)有問(wèn)題。
Angular Service Worker 會(huì)保證:正在運(yùn)行的應(yīng)用程序會(huì)繼續(xù)運(yùn)行和當(dāng)前應(yīng)用相同的版本。而如果在新的 Web 瀏覽器選項(xiàng)卡中打開(kāi)了該應(yīng)用的另一個(gè)實(shí)例,則會(huì)提供該應(yīng)用的最新版本。因此,這個(gè)新標(biāo)簽可以和原始標(biāo)簽同時(shí)運(yùn)行不同版本的應(yīng)用。
重要:
這種擔(dān)保比普通的 Web 部署模型提供的擔(dān)保還要更強(qiáng)一點(diǎn)。 如果沒(méi)有 Service Worker,則不能保證稍后在這個(gè)正在運(yùn)行的應(yīng)用中惰性加載的代碼 和其初始代碼的版本是一樣的。
Angular Service Worker 為什么可能會(huì)更改運(yùn)行中的應(yīng)用的版本有幾個(gè)有限的原因。 其中一些是因?yàn)槌鲥e(cuò)了:
Angular Service Worker 能知道在任何指定的時(shí)刻正在使用哪些版本,并清除那些沒(méi)有被任何選項(xiàng)卡使用的版本。
另一些可能導(dǎo)致 Angular Service Worker 在運(yùn)行期間改變版本的因素是一些正常事件:
SwUpdate
?服務(wù)請(qǐng)求立即激活這個(gè)更新。Angular Service Worker 是一個(gè)運(yùn)行在 Web 瀏覽器中的小腳本。有時(shí),這個(gè) Service Worker 也可能會(huì)需要更新,以修復(fù)錯(cuò)誤和增強(qiáng)特性。
首次打開(kāi)應(yīng)用時(shí)或在一段非活動(dòng)時(shí)間之后再訪(fǎng)問(wèn)應(yīng)用程序時(shí),就會(huì)下載 Angular Service Worker。如果 Service Worker 發(fā)生了變化,Service Worker 就會(huì)在后臺(tái)進(jìn)行更新。
Angular Service Worker 的大部分更新對(duì)應(yīng)用程序來(lái)說(shuō)都是透明的 - 舊緩存仍然有效,其內(nèi)容仍然能正常使用。但是,在 Angular Service Worker 中可能偶爾會(huì)有錯(cuò)誤修復(fù)或新功能,需要讓舊的緩存失效。這時(shí),應(yīng)用程序就從會(huì)網(wǎng)絡(luò)上透明地進(jìn)行刷新。
某些情況下,你可能想要完全繞過(guò) Service Worker,轉(zhuǎn)而讓瀏覽器處理請(qǐng)求。比如當(dāng)你要用到某些 Service Worker 尚不支持的特性時(shí)(比如報(bào)告文件上傳的進(jìn)度)。
要想繞過(guò) Service Worker,你可以設(shè)置一個(gè)名叫 ?ngsw-bypass
? 的請(qǐng)求頭或查詢(xún)參數(shù)。(這個(gè)請(qǐng)求頭或查詢(xún)參數(shù)的值會(huì)被忽略,可以把它設(shè)為空字符串或略去。)
偶爾,可能會(huì)需要檢查運(yùn)行中的 Angular Service Worker,以調(diào)查問(wèn)題或確保它在按設(shè)計(jì)運(yùn)行。瀏覽器提供了用于調(diào)試 Service Worker 的內(nèi)置工具,而且 Angular Service Worker 本身也包含了一些有用的調(diào)試功能。
Angular Service Worker 會(huì)在虛擬目錄 ?ngsw/
? 下暴露出調(diào)試信息。目前,它暴露的唯一的 URL 是 ?ngsw/state
?。下面是這個(gè)調(diào)試頁(yè)面中的一段范例內(nèi)容:
NGSW Debug Info:
Driver version: 13.3.7
Driver state: NORMAL ((nominal))
Latest manifest hash: eea7f5f464f90789b621170af5a569d6be077e5c
Last update check: never
=== Version eea7f5f464f90789b621170af5a569d6be077e5c ===
Clients: 7b79a015-69af-4d3d-9ae6-95ba90c79486, 5bc08295-aaf2-42f3-a4cc-9e4ef9100f65
=== Idle Task Queue ===
Last update tick: 1s496u
Last update run: never
Task queue:
* init post-load (update, cleanup)
Debug log:
第一行表示驅(qū)動(dòng)程序的狀態(tài):
Driver state: NORMAL ((nominal))
?NORMAL
?表示這個(gè) Service Worker 正在正常運(yùn)行,并且沒(méi)有處于降級(jí)運(yùn)行的狀態(tài)。
有兩種可能的降級(jí)狀態(tài):
降級(jí)狀態(tài) |
詳情 |
---|---|
EXISTING_CLIENTS_ONLY
|
這個(gè) Service Worker 沒(méi)有該應(yīng)用的最新已知版本的干凈副本。較舊的緩存版本可以被安全的使用,所以現(xiàn)有的選項(xiàng)卡將繼續(xù)使用較舊的版本運(yùn)行本應(yīng)用,但新的應(yīng)用將從網(wǎng)絡(luò)上加載。 |
SAFE_MODE
|
Service Worker 不能保證使用緩存數(shù)據(jù)的安全性。發(fā)生了意外錯(cuò)誤或所有緩存版本都無(wú)效。這時(shí)所有的流量都將從網(wǎng)絡(luò)提供,盡量少運(yùn)行 Service Worker 中的代碼。 |
在這兩種情況下,后面的括號(hào)注解中都會(huì)提供導(dǎo)致 Service Worker 進(jìn)入降級(jí)狀態(tài)的錯(cuò)誤信息。
這兩種狀態(tài)都是暫時(shí)的;它們僅在 ServiceWorker 實(shí)例 的生命周期內(nèi)保存。 瀏覽器有時(shí)會(huì)終止空閑的 Service Worker,以節(jié)省內(nèi)存和處理能力,并創(chuàng)建一個(gè)新的 Service Worker 實(shí)例來(lái)響應(yīng)網(wǎng)絡(luò)事件。 無(wú)論先前實(shí)例的狀態(tài)如何,新實(shí)例均以 ?NORMAL
?模式啟動(dòng)。
Latest manifest hash: eea7f5f464f90789b621170af5a569d6be077e5c
這是 Service Worker 所知道的應(yīng)用最新版本的 SHA1 哈希值。
Last update check: never
這表示 Service Worker 最后一次檢查應(yīng)用程序的新版本或更新的時(shí)間。“never” 表示 Service Worker 從未檢查過(guò)更新。
在這個(gè)調(diào)試文件范例中,這次更新檢查目前是已排期的,如下一節(jié)所述。
=== Version eea7f5f464f90789b621170af5a569d6be077e5c ===
Clients: 7b79a015-69af-4d3d-9ae6-95ba90c79486, 5bc08295-aaf2-42f3-a4cc-9e4ef9100f65
在這個(gè)例子中,Service Worker 擁有一個(gè)版本的應(yīng)用程序緩存并用它服務(wù)于兩個(gè)不同的選項(xiàng)卡。
注意:
這個(gè)版本哈希值是上面列出的“最新清單的哈?!薄?nbsp;它的兩個(gè)客戶(hù)運(yùn)行的都是最新版本。每個(gè)客戶(hù)都用瀏覽器中 ?Clients
?API 的 ID 列了出來(lái)。
=== Idle Task Queue ===
Last update tick: 1s496u
Last update run: never
Task queue:
* init post-load (update, cleanup)
空閑任務(wù)隊(duì)列是 Service Worker 中所有在后臺(tái)發(fā)生的未決任務(wù)的隊(duì)列。如果這個(gè)隊(duì)列中存在任何任務(wù),則列出它們的描述。在這個(gè)例子中,Service Worker 安排的任務(wù)是一個(gè)用于更新檢查和清除過(guò)期緩存的后期初始化操作。
最后的 tick/run 計(jì)數(shù)器給出了與特定事件發(fā)生有關(guān)的空閑隊(duì)列中的時(shí)間?!癓ast update run” 計(jì)數(shù)器顯示的是上次執(zhí)行空閑任務(wù)的時(shí)間?!癓ast update tick” 顯示的是自上次事件以來(lái)可能要處理的隊(duì)列的時(shí)間。
Debug log:
在 Service Worker 中出現(xiàn)的任何錯(cuò)誤都會(huì)記錄在這里。
Chrome 等瀏覽器提供了能與 Service Worker 交互的開(kāi)發(fā)者工具。這些工具在使用得當(dāng)時(shí)非常強(qiáng)大,但也要牢記一些事情。
像任何復(fù)雜的系統(tǒng)一樣,錯(cuò)誤或損壞的配置可能會(huì)導(dǎo)致 Angular Service Worker 以不可預(yù)知的方式工作。雖然它在設(shè)計(jì)時(shí)就嘗試將此類(lèi)問(wèn)題的影響降至最低,但是,如果管理員需要快速停用 Service Worker,Angular Service Worker 也包含多種故障保護(hù)機(jī)制。
要停用 Service Worker,請(qǐng)刪除或重命名 ?ngsw.json
? 文件。當(dāng) Service Worker 對(duì) ?ngsw.json
? 的請(qǐng)求返回 ?404
?時(shí),Service Worker 就會(huì)刪除它的所有緩存并注銷(xiāo)自己,本質(zhì)上就是自毀。
?@angular/service-worker
? NPM 包中還包含一個(gè)小腳本 ?safety-worker.js
?,當(dāng)它被加載時(shí)就會(huì)把它自己從瀏覽器中注銷(xiāo),并移除此 Service Worker 的緩存。這個(gè)腳本可以作為終極武器來(lái)擺脫那些已經(jīng)安裝在客戶(hù)端頁(yè)面上的不想要的 Service Worker。
重要:
你不能直接注冊(cè)這個(gè) Safety Worker,因?yàn)榫哂幸丫彺鏍顟B(tài)的舊客戶(hù)端可能無(wú)法看到一個(gè)新的、用來(lái)安裝 另一個(gè) worker 腳本的 ?index.html
?。
你必須在想要注銷(xiāo)的 Service Worker 腳本的 URL 中提供 ?safety-worker.js
? 的內(nèi)容, 而且必須持續(xù)這樣做,直到確定所有用戶(hù)都已成功注銷(xiāo)了原有的 Worker。 對(duì)大多數(shù)網(wǎng)站而言,這意味著你應(yīng)該永遠(yuǎn)為舊的 Service Worker URL 提供 這個(gè) Safety Worker。 這個(gè)腳本可以用來(lái)停用 ?@angular/service-worker
?(并移除相應(yīng)的緩存)以及任何其它曾在你的站點(diǎn)上提供過(guò)的 Service Worker。
重要:
Service Worker 無(wú)法在重定向后工作。你可能已經(jīng)遇到過(guò)這種錯(cuò)誤:?The script resource is behind a redirect, which is disallowed
?。
如果你不得不更改應(yīng)用的位置,就可能會(huì)出現(xiàn)問(wèn)題。如果你設(shè)置了從舊位置(比如 ?example.com
?)到新位置(比如 ?www.example.com
?)的重定向,則 Service Worker 將停止工作。同樣,對(duì)于完全從 Service Worker 加載該網(wǎng)站的用戶(hù),甚至都不會(huì)觸發(fā)重定向。老的 Worker(注冊(cè)在 ?example.com
?)會(huì)嘗試更新并將請(qǐng)求發(fā)送到原來(lái)的位置 ?example.com
?,該位置重定向到新位置 ?www.example.com
? 就會(huì)導(dǎo)致錯(cuò)誤 ?The script resource is behind a redirect, which is disallowed
?。
Copyright©2021 w3cschool編程獅|閩ICP備15016281號(hào)-3|閩公網(wǎng)安備35020302033924號(hào)
違法和不良信息舉報(bào)電話(huà):173-0602-2364|舉報(bào)郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號(hào)
聯(lián)系方式:
更多建議: