Progressive Web Apps - Part.3 U4 PWA 特性支持概覽

眾所周知,由於內核多樣化、版本碎片化、標準與草案落地情況以及各家私有方案的實現,導致前端開發大部分工作都是在做兼容性問題定位和修復,這些兼容性問題一般我們都稱之為「坑」,所以為了避免我們踩坑,我們要把 U4 內核對 PWA 特性的支持情況捋一遍。

離線類存儲 Cache

說到離線存儲,大家不得不會聯想到 Application Cache (已從 Web 標準刪除),因為在設計之初耦合了一些設想的業務場景進去,導致可擴展性和可控制性極差,本身實現之後也有一些 bug 導致無法更新,甚至還可以進行緩存投毒,從而達到長期劫持用戶客戶端緩存的目的。也有人專門寫了篇文章分析 Application Cache 的缺陷(Application Cache is a Douchebag)。ServiceWorker Cache 的能力卻與 Application Cache 越來越像,它能夠提供精細的存儲控制能力,並且 Fetch + Cache 已能較好的替代 Application Cache 的作用,即使在不支持 ServiceWorker 的瀏覽器(比如 Mobile Safari),通過支持 Fetch 和 Cache,也能較好的進行存儲控制,這估計也是 Fetch 和 Cache 會從 ServiceWorker 獨立出來的原因之一,所以我們接下來想討論的是 Cache API。

我們首先來看下 U4 內核對 Cache API 的支持程度:

Cache.add 方法不支持也沒關係,其實只是對 fetch and put 的操作做了層封裝,同樣,Cache.matchAll 也可以用 keys and match 來實現。這三個方法只是在後來 Chromium 的版本迭代中被實現了,後續版本升級了也同樣支持。Cache Api 的詳細介紹,後面單獨開一篇詳細說說。

網路請求 Fetch

說到網路請求,自然而然我們第一時間會想到的是 Ajax,使用 Ajax 最出名的項目當屬 Gmail 了,而 Ajax 的關鍵技術則是 XMLHttpRequest。我們為什麼需要新增Fetch API,而不是繼續擴展XHR呢?XMLHttpRequest雖然非常強大,但也並不完美,它在設計上存在缺陷,不符合職責分離原則,輸入、輸出和結果處理都混雜在一個對象裡面,會存在回調嵌套問題。Fetch API 是一個面向未來的 API,既現代又底層,有很多非常優雅的特性,Promise-based,Request/Response primitives,Support Streams 等等。那我們來看看 U4 內核對 Fetch API 的支持程度。

目前在 U4 內核上,還無法在 window 全局變數下使用 Fetch API,但是 Service Worker 已經支持 Fetch API。需要注意的是,Fetch API 對 301 跳轉的資源請求會導致失敗,參考《PWA 在餓了么的實踐經驗》PWA 在餓了么的實踐經驗 - 知乎專欄 提到的臨時解決方案:服務端避免 301 跳轉,或者 SW 中對存在 301 跳轉情況的資源做特殊處理。

FetchEvent.client 對象目前已被分離出 FetchEvent,獨立作為 Client 對象。

Request.cache 定義的緩存讀取規則,可以通過 Fetch + Cache 兩個 API 來實現,參考《The Offline Cookbook》。Request.redirect 雖然不支持,但是可以通過請求的狀態碼進行判斷執行相應的邏輯。

Response.ok 可以通過判斷狀態碼 status >= 200 & status < 300。Response.error 方法生成的實例對象,可以通過 new Response(null, {status: 0, statusText: 』}) 實現。Response.redirect 可以判斷 status 是否為 301、302、303、307、308,然後根據 url 重新實例化新的 Response 對象。其他的方法目前還沒有較好的替代實現方式。

Service Worker

Service Worker API 使用環境分主文檔環境和服務工作線程環境。主文檔環境的 Service Worker 需要從 ServiceWorkerContainer 獲取,navigator.serviceWorker 也就是 ServiceWorkerContainer,navigator.serviceWorker.controller 返回一個 Service Worker 對象。

其中 onmessage 方法可以通過 window.addEventListener(message, function() {}) 方式替換實現。

Service Worker 註冊成功後,會通過 Promise 方式返回 registration 實例對象,這個對象包含了以下的方法:

很遺憾,Google 提供的 PUSH 服務無法在大陸正常使用,各家瀏覽器因為隱私原因,都希望自建 PUSH 通道,目前 pushManager 還不支持。另外,由於 Background Sync(非標準化) 和 Notification 目前在 U4 內核上還不支持,相關的配套服務需要建設(同 PUSH 的情況,依賴的 Google 服務在中國大陸無法正常使用),所以 registration 相關的方法也是不支持的。鑒於這種情況,我們已經在抓緊時間建設大陸能夠使用的配套服務設施。

目前只有 Service Worker 的兩個生命周期事件 install 和 activate 才能產生 ExtendableEvent 對象實例,ExtendableEvent 與一般的 event 對象不同,它是為了 Service Worker 而專門設計的,確保在任何情況下,waitUntil 後能夠繼續執行下一步的邏輯。目前 U4 內核還不支持 ExtendableMessageEvent 和 ServiceWorkerMessageEvent。

ServiceWorker 環境的一些全局 context 方法基本上都支持,除了與 PUSH 和 Notification 相關的 API。

存在這麼一種業務場景,就是用戶當前停留在某一網頁,而此時這一網頁的 Notification 可能就不需要推送了。此時在 Service Worker 裡面,需要通過 Client API 獲取當前瀏覽器已打開的頁面(與 Service Worker 同域),匹配 type 為 window。Client 是 Service Worker 控制的窗口,可以是 window,也可以是 worker, share worker。不過由於支持程度有限,所以現在這種業務場景無法支持,能做的事也只能是將當前 Service Worker 與同域的 Client 進行關聯或者 Service Worker 與其控制頁面進行雙向通信。

至此,我們基本上已經把 U4 內核上的 PWA 相關特性大概地了解了一遍,後面我們再針對各個特性做進一步詳細的分析。


推薦閱讀:

Progressive Web Apps - Part.1 為什麼是 PWA?
SSR 架構項目實現離線可用(思路&案例)

TAG:前端开发 | 浏览器内核 | pwa |