iOS 開發 H5 離線方案


目前本人所負責的APP中使用了大量的h5頁面,所以在使用h5資源時,性能優化是作為客戶端開發的重點。雖然現在APP已經使用了 webView自帶緩存、硬件加速、等緩存方案,但是針對首次打開速度仍然很慢。因此項目組提出了h5離線方案。

h5 離線優點

1.節省流量:下載資源包為非一次性消耗資源,每次加載 h5 都可以從本地獲取。
2.秒開:不需要通過遠端加載 h5 資源。
3.防劫持:不需要從遠端加載 h5 資源。

實現方案

離線方案對於不同開發人員有不同的實現方案,本人提供的方案也不一定是最好的方案,所以參考即可。
1.將 h5 需要的資源下載到本地。
2.根據 h5加載鏈接路徑和本地資源路徑做一個映射
3.加載時攔截 h5 資源,根據 h5 遠程路徑獲取本地路徑,直接加載本地數據。
目前使用的映射方案,例如:

遠程鏈接:http://www.baidu.com?path1/path2/path3/index.html
對應的本地路徑:根路徑/www.baidu.com/path1/path2/path3/index.html

注:其中本地路徑中:www.baidu.com 和 path1、path2 等都為文件夾名稱。其中最后的 index.html 為具體文件。

ios 端實現方案

此處需要使用 URLProtocol 進行攔截。需要使用到 URLProtocol 的協議方法

func canInit(with request: URLRequest) -> Bool
func canonicalRequest(for request: URLRequest) -> URLRequest
func startLoading()
func stopLoading()

解決WKWebView 通信問題

在使用 URLProtocol 進行資源攔截的時候,如果使用 UIWebView 則沒有問題。但是如果使用WKWebView 則會有問題:URLProtocol 無法攔截到 WKWebView 上的 request 請求。此時,需要使用私有 api ,通過注冊 http(s) 則,可以使用 URLProtocol 攔截到 WKWebView 的 request。

類
browsingContextController 
注冊 scheme
registerSchemeForCustomProtocol
取消注冊 scheme
unregisterSchemeForCustomProtocol

WKWebView 后遺症:body 丟失問題

WKWebView 那些坑
蘋果源碼
通過私有 api 雖然可以攔截到 http(s) 請求,但是對於 http(s) 的 post 請求則會丟失 body。原因為app 和 WKWebView 為兩個進程,通過注冊 scheme 可以將 WKWebView 中的 request 通過 IPC 通信傳遞到 app 進程中,所以才可以攔截到 request。但是在經過 IPC 通信時,會將 request 進行 encode,在 encode 的過程中,為了提高性能以及安全會將 body 和 bodyStream 進行丟失,從而造成 post 請求丟失 body 的情況。
為了解決 body 丟失問題,可以有以下方案
注:post 請求與是否攔截成功無關,是由於走 IPC 通信丟失的。

方案 1:使用 UIWebView

使用 UIWebView 不會造成跨進程,也就不需要跨進程通信,也就不需要注冊 scheme 等,所以可以完美解決,但是如果必須使用 WKWebView,則使用其他方案。

方案 2:自定義 scheme

因為注冊 http(s) 才能讓 http(s) 請求通過 IPC 通信,而造成 body 丟失,所以可以選擇使用自定義 scheme 的方案。這樣就會讓自定義的 scheme 通過 IPC 通信。
優點:
1、可以避開http(s) 經過 IPC 通信,從而避免丟失 body
缺點:
1、需要將h5 離線資源的 shceme 都換為自定義的 scheme
2、需要后端做大量工作

方案 3:WKURLSchemHandler

該方案和方案 2 一樣,都是需要自定義 scheme 並且 WKURLSchemHandler 需要 iOS11 以后才能使用

方案 4:使用 hook-ajax 的方案

hook-ajax方案原理為使用一段 js 代碼,將 xmlhttprequest 進行代理一份,這樣避免 body 丟失。
可行性:
1.該方案需要注入一段 js 代碼,網上有
2.作為客戶端開發,最好需要對 js 代碼有一定的了解
3.僅僅對 xmlhttprequest 有效,針對 fetch 請求無效。
注:hook-ajax 僅對 XMLHTTPRequest 有效,但是目前使用的 h5 資源中使用了大量的 fetch 請求所以暫時沒有使用。

方案 5:使用頻繁 register 和 unregister 的方案

因為當時 app 每次加載 url 都是使用一個新的 controller,所以可以在每次加載controller 時判斷是否為離線資源鏈接,如果為離線資源鏈接,則 register scheme,否則 unregister scheme。
注:考慮到 h5 資源內部有一些上報埋點等 post 請求。如果此時 register scheme 則會造成埋點 post 請求丟失。

方案 6:使用本地服務器

該方案持續研究中。。。

總結

h5 離線開發可以提高加載 h5 速度,防止服務器劫持等優點,但是攔截 h5 資源需要使用 URLprotocol,但是 攔截 WKWebView 會出現 post 請求丟失 body 問題,所以使用 wkwebview 需要首先解決 post 請求 body 丟失問題。丟失原因為:
1.攔截 WKWebView 的 h5 資源需要執行私有 api,進行 register scheme 和 unregister scheme.
2.執行私有 api 會走 IPC 通信,造成 post 請求丟失 body 的情況。

注:鑒於post 請求丟失 body 問題和目前現有的解決方案不能 100% 避免帶來的問題,或解決了改問題可能帶來其他的問題,所以iOS 端暫未實行 h5 離線方案


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM