一餐曹、需求背景
為了提高用戶體驗,針對 APP 內(nèi)嵌的 H5 頁面,有一種比較常見且有效的方式就是接入離線包坪仇,通過把頁面加載需要的靜態(tài)資源提前下載到客戶端本地,避免頁面加載時靜態(tài)資源網(wǎng)絡(luò)請求的開銷垃你,從而來提高頁面的加載速度椅文。
二喂很、知識儲備
1.webview加載h5的過程:
打開一個h5頁面通常會有較長時間出現(xiàn)白屏,以及幾秒后出現(xiàn)內(nèi)容,在這幾秒中到底做了什么事情?因為在這其中做了很多事情,大約包括:
初始化 webview -> 請求頁面 -> 下載數(shù)據(jù) -> 解析HTML -> 請求 js/css 資源 -> dom 渲染 -> 解析 JS 執(zhí)行 -> JS 請求數(shù)據(jù) -> 解析渲染 -> 下載渲染圖片
一般來說, Webview渲染需要經(jīng)過下面的幾個步驟:
- 解析HTML文件
- 加載 JavaScript 和 CSS 文件
- 解析并執(zhí)行 JavaScript
- 構(gòu)建 DOM 結(jié)構(gòu)
- 加載圖片等資源
- 頁面加載完畢
一般頁面是在dom渲染后才能展示,h5首屏渲染白屏問題的原因關(guān)鍵在于,如何去優(yōu)化請求下載->渲染之間的耗時成了重點.
2.WKWebView攔截請求:
研究了業(yè)內(nèi)已有的 WKWebView
請求攔截方案,主要分為如下兩種:
NSURLProtocol
NSURLProtocol是一個抽象類皆刺,作為URL Loading System
系統(tǒng)的一部分少辣,能夠幫助我們攔截所有的URL Loading System
的請求,在此進行各種自定義的操作羡蛾,是網(wǎng)絡(luò)層實現(xiàn)AOP(面向切面編程)的利器漓帅。
WKURLSchemeHandler
WKURLSchemeHandler
是 iOS 11 引入的新特性,負責自定義請求的數(shù)據(jù)管理痴怨,如果需要支持 scheme 為 http 或 https請求的數(shù)據(jù)管理則需要 hook WKWebView
的 handlesURLScheme
: 方法忙干,然后返回NO即可。
兩種方案對比
| NSURLProtocol | WKURLSchemeHandler |
| 隔離性 | 一經(jīng)注冊就是全局開啟浪藻。使用了 NSURLProtocol
的方式后會導(dǎo)致應(yīng)用內(nèi)合作的三方頁面也會被攔截從而被污染捐迫。 | 可以以頁面為維度進行隔離,可以通過WKWebViewConfiguration
進行配置爱葵。 |
| 穩(wěn)定性 | NSURLProtocol
攔截過程中會丟失 Body | WKURLSchemeHandler
在 iOS 11.3 之前 (不包含) 也會丟失 Body施戴,在 iOS 11.3 以后 WebKit 做了優(yōu)化只會丟失 Blob(二進制的一種數(shù)據(jù)類型) 類型數(shù)據(jù) |
| 一致性 | WKWebView
發(fā)出的請求被 NSURLProtocol
攔截后行為可能發(fā)生改變,比如想取消 video 標簽的視頻加載一般都是將資源地址 (src) 設(shè)置為空萌丈,但此時 stopLoading
方法卻不會調(diào)用 | 表現(xiàn)正常 |
****結(jié)論:****WKURLSchemeHandler
在隔離性赞哗、穩(wěn)定性、一致性上表現(xiàn)優(yōu)于NSURLProtocol
辆雾,但是想在生產(chǎn)環(huán)境投入使用必須要解決 Body 丟失的問題懈玻。
三、技術(shù)實現(xiàn)
H5離線包的基本原理是將html乾颁、js涂乌、css、圖片等靜態(tài)資源打包成壓縮包英岭,然后下載到客戶端并解壓湾盒,H5加載時直接從本地讀取靜態(tài)資源文件,減少網(wǎng)絡(luò)請求诅妹,提高速度罚勾。
3.1 總體流程
客戶端啟動后,先去遠程配置服務(wù)器拉取離線包相關(guān)的功能配置吭狡,然后檢查更新尖殃,如果有更新則下載離線包。webview加載時划煮,如果本地緩存命中送丰,則從本地磁盤加載html、js弛秋、css器躏、圖片等靜態(tài)資源俐载。。
3.2 離線包和非離線包對比
webView最簡單的做法是直接通過URL去加載一個線上頁面登失。當從瀏覽器輸入一個URL到頁面中間經(jīng)歷了什么?
從上面看出用戶加載一個web頁面都要經(jīng)過多次網(wǎng)絡(luò)加載遏佣,中間是極易受到網(wǎng)絡(luò)波動的影響,在無法保證頁面加載的時長和成功率的情況下揽浙,會很大影響用戶體驗状婶。
于是把html、js馅巷、css都抽離出來做成離線包放在本地太抓,這樣加載一個web頁面的過程就如下圖
3.3 行業(yè)方案
| 方案名 | 優(yōu)點 | 缺點 | 備注 |
| 加載本地路徑 | 簡單可靠,無需hook和調(diào)用私有API | 有跨域問題令杈,影響cookie和localstorage走敌,H5需做少量改動 | 貨拉拉方案 |
| 請求攔截 | 不修改加載URL,沒有跨域問題逗噩,且支持網(wǎng)頁部分資源離線化掉丽,靈活性和兼容性好 | iOS端目前提供的NSURLProtocol和WKSURLSchemehandler攔截方案有缺陷,前期實現(xiàn)成本高 | 網(wǎng)易云音樂方案zhuanlan.zhihu.com/p/347592487 |
| 本地Web Server | 兼容性好 | 對客戶端耗電和CPU性能有影響 | 暫未發(fā)現(xiàn)有公司采用异雁,juejin.cn/entry/68449… |
| Service Worker | 前端兼容性好 | iOS端WKWebView不提供官方支持捶障,實現(xiàn)技術(shù)難度大 | 愛奇藝方案zhuanlan.zhihu.com/p/148931732 |
四、接入工作:
目前我們采用的方案是:本地文件路徑(file://協(xié)議)纲刀,可能存在一些問題项炼,所以在接入離線包前,我們需要對 H5 項目進行改造示绊。
4.1 問題和解決辦法
| 存在問題 | 解決方法 |
| cgi 請求跨域問題 | 在網(wǎng)關(guān)或者后端服務(wù)的跨域請求頭增加 null 域支持 |
| cookie 跨域問題 | 我們接入的項目中無 cookie 操作锭部,如果有的話,需要改成用請求 header 的方式面褐。 |
| localStorage 跨域問題 | 我們接入的項目中 localStorage 不涉及域名隔離問題拌禾,如果有需要,可以采取調(diào)用原生的方式來做前端存儲展哭。 |
| 資源引用的絕對路徑在離線包模式下不支持 | 改成相對路徑 |
4.2 接口跨域處理
目前我們線上模式的請求鏈路大致為H5 → 網(wǎng)關(guān) → 后臺服務(wù)
湃窍,大部分的項目都是通過一級通用網(wǎng)關(guān)來處理跨域問題,但是由于離線包模式下需要網(wǎng)關(guān)設(shè)置允許 origin: null
的場景跨域匪傍,直接在通用網(wǎng)關(guān)處理可能存在安全問題您市。
目前我們的實現(xiàn)如下:
通過hook header["是否是接口請求"]判斷是否攔截請求,然后替換根據(jù)header["x-request-host"]來替換當前的host役衡;一句戶就是app幫助h5請求網(wǎng)絡(luò)再把結(jié)果回調(diào)給h5茵休。
五、參考資料
蘋果官方文檔https://developer.apple.com/documentation/webkit/wkwebview?language=objc
貨拉拉H5接入離線包的實踐總結(jié) zhuanlan.zhihu.com/p/538288299
WKWebView之離線加載以及遇到的問題https://blog.csdn.net/Lu_Ca/article/details/125723040
WKWebview秒開實踐分享及問題解決方案(抖音)https://juejin.cn/post/6887161842406260744#heading-6
WKWebView 請求攔截探索與實踐(網(wǎng)易)https://juejin.cn/post/6922625242796032007