摘要:配合的項(xiàng),能夠?qū)崿F(xiàn)緩存外部資源的功能。允許接受來(lái)自的消息,默認(rèn)值為。檢查新版本的的更新信息。這也是我在研究階段直接使用時(shí)所發(fā)現(xiàn)的問(wèn)題。建議僅在生產(chǎn)模式內(nèi)使用。
談起PWA,許多人可能還只停留在“了解”的層面,比較少在實(shí)踐中真正地嘗試過(guò),更多的僅僅是對(duì)著網(wǎng)上的教程和例子大概玩過(guò)。然而,網(wǎng)絡(luò)上的例子多是簡(jiǎn)單的demo,鮮有與真正的開發(fā)相結(jié)合,例如和webpack的工程化結(jié)合。這篇文章將會(huì)從一個(gè)webpack plugin出發(fā),談一談如何使用這個(gè)名為offline-plugin的webpack插件輕松實(shí)現(xiàn)PWA。
由于PWA相關(guān)的文章太多,所以本文不再對(duì)“什么是PWA”,“PWA的生命周期”等基礎(chǔ)內(nèi)容再次贅述。
offline-plugin相關(guān)鏈接:
offline-plugin
demo
一、自動(dòng)生成service-worker.jsPWA的核心可謂是service-worker(以后簡(jiǎn)稱SW),任何一個(gè)PWA都有且只有一個(gè)service-worker.js文件,用于為SW添加資源列表,進(jìn)行注冊(cè)、激活等生命周期操作。但是在webpack構(gòu)建的項(xiàng)目中,生成一個(gè)service-worker.js可能會(huì)面臨兩個(gè)較大的問(wèn)題:
1、webpack生成的資源多會(huì)生成一串hash,sw的資源列表里面需要同步更新這些帶hash的資源;
2、每次更新代碼,都需要通過(guò)更新sw文件版本號(hào)來(lái)通知客戶端對(duì)所緩存的資源進(jìn)行更新。(其實(shí)只要這一次的sw代碼和上一次的sw代碼不一樣即可觸發(fā)更新,但使用明確的版本號(hào)會(huì)更加合適)。
看到這你可能已經(jīng)想到,萬(wàn)能的webpack社區(qū)是否已經(jīng)提供了相應(yīng)的plugin來(lái)幫我們自動(dòng)處理這些事情呢?答案是肯定的。除了官方推薦的sw-precache-webpack-plugin之外,還有我們今天的主角offline-plugin。
相比與sw-precache-webpack-plugin,個(gè)人認(rèn)為offline-plugin具有如下優(yōu)點(diǎn):
1、更多的可選配置項(xiàng),滿足更加細(xì)致的配置要求;
2、更為詳細(xì)的文檔和例子;
3、更新頻率相對(duì)更高,star數(shù)更多;
4、自動(dòng)處理生命周期,用戶無(wú)需糾結(jié)生命周期的坑;
*5、支持AppCache;
6、自動(dòng)生成manifest文件。
...
二、基本使用 安裝npm install offline-plugin [--save-dev]初始化
第一步,進(jìn)入webpack.config:
// webpack.config.js example var OfflinePlugin = require("offline-plugin"); module.exports = { // ... plugins: [ // ... other plugins // it"s always better if OfflinePlugin is the last plugin added new OfflinePlugin() ] // ... }
第二步,把runtime添加到你的入口js文件當(dāng)中:
require("offline-plugin/runtime").install();
ES6/Babel/TypeScript
import * as OfflinePluginRuntime from "offline-plugin/runtime"; OfflinePluginRuntime.install();
經(jīng)過(guò)上面的步驟,offline-plugin已經(jīng)集成到項(xiàng)目之中,通過(guò)webpack構(gòu)建即可。
三、配置前面說(shuō)過(guò),offline-plugin支持細(xì)致的配置,以滿足不同的需求。下面將介紹幾個(gè)比較常用的配置項(xiàng),方便大家進(jìn)一步使用。
Caches: "all" | Object
告訴插件應(yīng)該緩存什么東西,并以何種方式進(jìn)行緩存 `all`: 意味著所有webpack構(gòu)建出來(lái)的資源,以及在`externals`選項(xiàng)中的資源都會(huì)被緩存。 `Object`: 包含三個(gè)數(shù)組或正則的配置對(duì)象(`main`, `additional`, `optional`),它們都是可選的,且默認(rèn)為空。 默認(rèn):`all`。
externals: Array
允許開發(fā)者指定一些外部資源(比如CDN引用,或者不是通過(guò)webpack生成的資源)。配合`Caches`的`additional`項(xiàng),能夠?qū)崿F(xiàn)緩存外部資源的功能。 默認(rèn):`null` 舉例:`["fonts/roboto.woff"]`
ServiceWorker: Object | null | false
該對(duì)象包含多個(gè)配置項(xiàng),這里僅列舉最常用的。 `events`:布爾值。允許runtime接受來(lái)自sw的消息,默認(rèn)值為false。 `navigateFallbackURL`:當(dāng)一個(gè)URL請(qǐng)求從緩存或網(wǎng)絡(luò)都無(wú)法被獲取時(shí),將會(huì)重定向到該選項(xiàng)所指向的URL。
AppCache: Object | null | false
`offline-plugin`默認(rèn)支持`AppCache`,但是`AppCache`草案已經(jīng)被web標(biāo)準(zhǔn)所廢棄,不建議使用。 但是由于仍然有部分瀏覽器支持,所以插件默認(rèn)提供這個(gè)功能。四、runtime
上一節(jié)介紹了offline-plugin在webpack當(dāng)中的配置,這一節(jié)將介紹runtime的一些用法。
若要使offline-plugin生效,用戶必須在入口js文件中通過(guò)runtime進(jìn)行初始化操作:
// 通過(guò)AMD方式 require("offline-plugin/runtime").install(); // 或者通過(guò)ES6/Babel/TypeScript方式 import * as OfflinePluginRuntime from "offline-plugin/runtime"; OfflinePluginRuntime.install();
OfflinePluginRuntime對(duì)象提供了下列三個(gè)方法:
install(options: Object)
開啟ServiceWorker/AppCache的安裝流程。這個(gè)方法是安全的,并且必須在頁(yè)面初始化的時(shí)候就被調(diào)用。另外請(qǐng)勿把它放在任何的條件語(yǔ)句之內(nèi)。(這句話不全對(duì),在后面的降級(jí)方案里面會(huì)詳細(xì)介紹)
applyUpdate()
接受當(dāng)前所安裝的sw的更新信息。
update()
檢查新版本的ServiceWorker/AppCache的更新信息。
runtime.install()方法接受一個(gè)配置對(duì)象參數(shù),用于處理sw各個(gè)生命周期里面的事件:
onInstalled
當(dāng)ServiceWorker/AppCache被install時(shí)執(zhí)行,可用于展示“APP已經(jīng)支持離線訪問(wèn)”。
onUpdating
AppCache不支持該方法 當(dāng)更新信息被獲取且瀏覽器正在進(jìn)行資源更新時(shí)觸發(fā)。在這個(gè)時(shí)刻,一些資源正在被下載。
onUpdateReady
當(dāng)`onUpdating`事件完成時(shí)觸發(fā)。這時(shí),所有資源都已經(jīng)下載完畢。 通過(guò)調(diào)用`runtime.applyUpdate()`方法來(lái)觸發(fā)更新。
onUpdateFailed
當(dāng)`onUpdating`事件因?yàn)槟承┰蚴r(shí)觸發(fā)。 這時(shí)沒(méi)有任何資源被下載,同時(shí)所有的資源更新進(jìn)程都應(yīng)該被取消或跳過(guò)。
onUpdated
當(dāng)更新被接受時(shí)觸發(fā)。五、降級(jí)方案
當(dāng)某些時(shí)候我們需要撤掉sw進(jìn)行降級(jí)的時(shí)候,我們需要主動(dòng)注銷sw。然而offline-plugin默認(rèn)沒(méi)有提供注銷sw的unregister()方法,所以我們需要自己實(shí)現(xiàn)。
其實(shí)要主動(dòng)注銷sw非常簡(jiǎn)單,我們可以直接調(diào)用ServiceWorkerContainer.getRegistrations()方法來(lái)拿到registration實(shí)例,然后調(diào)用registration.unregister()方法即可,具體代碼如下:
if ("serviceWorker" in navigator) { navigator.serviceWorker.getRegistration().then((registration) => { registration && registration.unregister().then((boolean) => { boolean ? alert("注銷成功") : alert("注銷失敗") }); }) }
在調(diào)用該方法后,sw已經(jīng)被注銷,刷新一下頁(yè)面就能看到資源是重新從網(wǎng)絡(luò)獲取的了。
在真實(shí)的生產(chǎn)環(huán)境中,我們可以通過(guò)調(diào)用接口,來(lái)決定是否使用降級(jí)方案:
fetch(URL).then((switch) => { if (switch) { OfflinePluginRuntime.install() } else { if ("serviceWorker" in navigator) { navigator.serviceWorker.getRegistration().then((registration) => { registration && registration.unregister().then((boolean) => { boolean ? alert("注銷成功") : alert("注銷失敗") }) }) } } })六、遇到的坑
在具體實(shí)踐中,遇到一個(gè)比較大的坑,就是sw.js文件的更新。
在service worker的設(shè)計(jì)中,瀏覽器每一次加載站點(diǎn)的URL,都會(huì)重新請(qǐng)求一遍sw.js。若發(fā)現(xiàn)這一次的sw.js內(nèi)容和上一次的不一樣,就會(huì)判定為資源更新,重新觸發(fā)sw的生命周期。然而,sw.js也是一個(gè)普通的js資源文件,會(huì)默認(rèn)使用服務(wù)器設(shè)置的expired時(shí)間,也就是它的max-age。在理解了service worker的設(shè)計(jì)后,我們不難發(fā)現(xiàn),sw.js的max-age應(yīng)該盡可能短,以便瀏覽器能夠及時(shí)更新資源列表。
這也是我在研究階段直接使用http-server時(shí)所發(fā)現(xiàn)的問(wèn)題。后來(lái)在官方的例子中,我發(fā)現(xiàn)npm script里面是這么寫的:
"start": "http-server ./dist -p 7474 -c no-cache"
直接指定了所有資源都不使用緩存,這一點(diǎn)值得我們注意。
另外,webpack-dev-server里無(wú)法正常使用offline-plugin,因?yàn)樗枰唧w的文件去生成sw.js,但是通過(guò)webpack-dev-server構(gòu)建的項(xiàng)目,其文件是存放在內(nèi)存中的,所以無(wú)法和offline-plugin正常搭配使用。建議僅在生產(chǎn)模式內(nèi)使用offline-plugin。
七、添加到主屏手機(jī)瀏覽器都提供了“添加到主屏”的功能,但普通的網(wǎng)站添加到主屏,僅僅是把網(wǎng)站的書簽放到桌面。如果要想把網(wǎng)站以PWA的形式添加到主屏,我們需要一個(gè)manifest.json文件:
{ "name": "offline-plugin", "icons": [ { "src": "/android-chrome-192x192.png", "sizes": "192x192", "type": "image/png" }, { "src": "/android-chrome-512x512.png", "sizes": "512x512", "type": "image/png" } ], "theme_color": "#181743", "background_color": "#181743", "start_url": "/", "display": "standalone" }
然后,把這個(gè)manifest.json和其他靜態(tài)資源一并打包到網(wǎng)站根目錄即可:
示例地址:
打開chrome開發(fā)者工具,進(jìn)入到Application一列,選擇Manifest,就可以看到效果了:
截止到目前(2017年8月15日),我所使用的iOS10.3.2版本的iPhone7手機(jī),已經(jīng)支持PWA了,效果如下:
經(jīng)過(guò)查閱大量的資料,到目前為止,iOS并不支持PWA,但是可以通過(guò)在html里面添加幾個(gè)標(biāo)簽,實(shí)現(xiàn)web頁(yè)面和原生APP相似的體驗(yàn)效果:
應(yīng)用圖標(biāo): 啟動(dòng)畫面: 應(yīng)用名稱: 全屏效果: 設(shè)置狀態(tài)欄顏色:
使用safari打開
添加到主屏后打開
離線后從主屏打開
打開任務(wù)管理器
可以看到,PWA無(wú)論從表現(xiàn)還是功能,都像一個(gè)獨(dú)立的APP那樣存在。
八、尾聲原來(lái)一直以為蘋果對(duì)PWA支持不好,但通過(guò)這次實(shí)踐,可以知道其實(shí)PWA也取得了極大的推進(jìn),開發(fā)者們可以開心地搭建自己的PWA啦!
結(jié)論不能下太早。。。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/91391.html
摘要:前端日?qǐng)?bào)精選理解的專題之偏函數(shù)譯理解事件驅(qū)動(dòng)機(jī)制游戲開發(fā)前端面試中的常見(jiàn)的算法問(wèn)題發(fā)布中文前端頁(yè)面?zhèn)鲄⑸袏y產(chǎn)品技術(shù)刊讀基礎(chǔ)系列二之實(shí)現(xiàn)大轉(zhuǎn)盤抽獎(jiǎng)掘金指南眾成翻譯編程插入排序眾成翻譯源碼講解函數(shù)技術(shù)風(fēng)暴初體驗(yàn)個(gè)人文 2017-08-16 前端日?qǐng)?bào) 精選 理解 JavaScript 的 async/awaitJavaScript專題之偏函數(shù)[譯]理解 Node.js 事件驅(qū)動(dòng)機(jī)制Pokem...
摘要:簡(jiǎn)稱,是提升的體驗(yàn)的一種新方法,能給用戶原生應(yīng)用的體驗(yàn)。能做到原生應(yīng)用的體驗(yàn)不是靠特指某一項(xiàng)技術(shù),而是經(jīng)過(guò)應(yīng)用一些新技術(shù)進(jìn)行改進(jìn),在安全性能和體驗(yàn)三個(gè)方面都有很大提升,本質(zhì)上是,借助一些新技術(shù)也具備了的一些特性,兼具和的優(yōu)點(diǎn)。 Progressive Web App, 簡(jiǎn)稱 PWA,是提升 Web App 的體驗(yàn)的一種新方法,能給用戶原生應(yīng)用的體驗(yàn)。 PWA 能做到原生應(yīng)用的體驗(yàn)不是靠...
摘要:現(xiàn)在表示公開支持。一旦安裝完成,如果注冊(cè)的沒(méi)有變化,則顯示為已激活的生命周期結(jié)束。一旦安裝這步完成,便會(huì)激活,并控制在其范圍內(nèi)的一切。目前還在草案狀態(tài),僅火狐和谷歌瀏覽器支持此特性。 PWA初探 什么是PWA PWA(Progressive Web Apps):漸進(jìn)式 Web app PWA 旨在增強(qiáng) Web 體驗(yàn),能讓用戶在訪問(wèn)一個(gè)web的時(shí)候感覺(jué)在使用app一樣。 PWA可以看作是...
摘要:而且,在接下來(lái)頁(yè)面的異步請(qǐng)求中,還能進(jìn)行緩存嘗試這里配置的文件清單在安裝激活階段不會(huì)進(jìn)行緩存,只有在監(jiān)聽到網(wǎng)絡(luò)請(qǐng)求的時(shí)候才進(jìn)行緩存。 基本知識(shí)普及請(qǐng)參考https://www.jianshu.com/p/623...https://zhuanlan.zhihu.com/p/... 下面簡(jiǎn)單介紹一下插件的使用以下是我在項(xiàng)目中使用的配置webpack.prod.conf.js中 { ...
摘要:本篇不包含所有坑,暫時(shí)只記錄自己踩到的部分坑一安裝安裝最新版本安裝新增依賴這個(gè)在中,本身和它的是在同一個(gè)包中,中將兩個(gè)分開管理。我記錄下自己更新這個(gè)的過(guò)程,以下前半部分可以直接跳過(guò)。以下記錄踩坑過(guò)程。 本篇不包含所有坑,暫時(shí)只記錄自己踩到的部分坑 一.安裝 安裝webpack4最新版本 npm install --save-dev webpack@4 安裝新增依賴 webpack-c...
閱讀 2693·2021-09-22 15:25
閱讀 3070·2021-09-14 18:03
閱讀 1346·2021-09-09 09:33
閱讀 1809·2021-09-07 09:59
閱讀 3022·2021-07-29 13:50
閱讀 1577·2019-08-30 15:44
閱讀 1793·2019-08-29 16:22
閱讀 1365·2019-08-29 12:49