行到水窮處,坐看雲起時css
原文連接html
從用戶角度,相比Native頁面,H5頁面的體驗問題主要有兩點:前端
這裏討論的是:第一點,怎樣減小白屏時間。web
經過Webview打開H5頁面,請求並獲得 HTML、CSS 和 JavaScript 等資源並對其進行處理從而渲染出 Web 頁面。後端
初始化Webview
-> 請求頁面
-> 下載數據
-> 解析HTML
-> 請求 js/css 資源
->DOM 渲染
-> 解析 JS 執行
-> JS 請求數據
-> 解析渲染
-> 下載渲染圖片
-> 頁面完整展現
對H5頁面的渲染,主要包括:渲染樹構建、佈局及繪製,具體可分爲:瀏覽器
處理 HTML 標記並構建 DOM 樹。緩存
處理 CSS 標記並構建 CSSOM(CSS Object Model) 樹。性能優化
將 DOM 與 CSSOM 合併成一個渲染樹。bash
根據渲染樹來佈局,以計算每一個節點的幾何信息。網絡
將各個節點繪製到屏幕上。
說明:這五個步驟並不必定一次性順序完成。若是 DOM 或 CSSOM 被修改,以上過程須要重複執行,這樣才能計算出哪些像素須要在屏幕上進行從新渲染。實際頁面中,CSS 與 JavaScript 每每會屢次修改 DOM 和 CSSOM。具體參考:DOM渲染機制與常見性能優化
下降請求量:合併資源,減小 HTTP 請求數,minify / gzip 壓縮,webP,lazyLoad。
加快請求速度:預解析DNS,減小域名數,並行加載,CDN 分發。
緩存:HTTP 協議緩存請求,離線緩存 manifest,離線數據緩存localStorage。
渲染:JS/CSS優化,加載順序,服務端渲染,pipeline。
複製代碼
因爲是接入第三方的H5頁面,接入離線包方案,須要比較繁雜的商務溝通和技術挑戰(業務邏輯和代碼超級詭異),臨時採用以下優化方案。
JS文件
和CSS文件
等資源放在一個URL地址(和業務url同域名);首次初始化Webview,須要初始化瀏覽器內核,須要的時間在400ms這個量級;二次初始化時間在幾十ms這個量級;
根據此特徵:選擇在APP 啓動後X秒,預建立(初始化)一個 Webview 而後釋放,這樣等使用到 H5 模塊,再加載 Webview時,加載時間也少了很多。
結合步驟一中預加載公共資源,也須要Webview,因此選擇在加載公共資源包時候,首次初始化Webview,加載資源,而後釋放。
因爲第三方業務H5不少問題,和人力上不足;不得不須要客戶端強行配合優化,在產品的要求下,不得不採用以下方案,方案的前提是:業務H5儘量少修改,甚至不修改,客戶端還要保證首屏加載快;
離線包方案纔是業務主流的H5加載優化方案,很是建議在客戶端團隊和前端團隊推廣,相似預建立Webview加載H5不該該成爲主流。
將每一個獨立的H5功能模塊,相關HTML、Javascript、CSS 等頁面內靜態資源打包到一個壓縮包內,客戶端能夠下載該離線包到本地,而後打開Webview,直接從本地加載離線包,從而最大程度地擺脫網絡環境對 H5 頁面的影響。
離線包能夠提高用戶體驗(頁面加載更快),還能夠實現動態更新(在推出新版本或是緊急發佈的時候,能夠把修改的資源放入離線包,經過更新配置讓應用自動下載更新)
引用bang的離線包方案,簡單描述以下:
後端使用構建工具把同一個業務模塊相關的頁面和資源打包成一個文件,同時對文件加密/簽名。
客戶端根據配置表,在自定義時機去把離線包拉下來,作解壓/解密/校驗等工做。
根據配置表,打開某個業務時轉接到打開離線包的入口頁面。
攔截網絡請求,對於離線包已經有的文件,直接讀取離線包數據返回,不然走 HTTP 協議緩存邏輯。
離線包更新時,根據版本號後臺下發兩個版本間的 diff 數據,客戶端合併,增量更新。
說明:目前WKWebView已經能成爲主流,可是WKWebView在實現離線包方案時,攔截網絡請求有坑。
//蘋果開源的 WebKit2 源碼暴露了私有API:
+ [WKBrowsingContextController registerSchemeForCustomProtocol:]
//經過註冊 http(s) scheme 後 WKWebView 將可使用 NSURLProtocol 攔截 http(s) 請求:
Class cls = NSClassFromString(@"WKBrowsingContextController」); SEL sel = NSSelectorFromString(@"registerSchemeForCustomProtocol:"); if ([(id)cls respondsToSelector:sel]) { // 註冊http(s) scheme, 把 http和https請求交給 NSURLProtocol處理 [(id)cls performSelector:sel withObject:@"http"]; [(id)cls performSelector:sel withObject:@"https"]; } 複製代碼
**說明1:**名目張膽使用私有API,是過不了AppStore審覈的,具體使用什麼辦法,想來你也懂(hun xiao)。
說明2:一旦打開ATS開關:Allow Arbitrary Loads 選項設置爲NO,經過 registerSchemeForCustomProtocol 註冊了 http(s) scheme,WKWebView 發起的全部 http(s) 網絡請求將被阻塞(即使將Allow Arbitrary Loads in Web Content 選項設置爲YES);
說明3:iOS11以後能夠經過WKURLSchemeHandler去完成對WKWebView
的請求攔截,不須要再調用私有API解決上述問題了。
dynamic://www.dynamicalbumlocalimage.com/
,經過 NSURLProtocol 攔截這個請求並加載離線數據。CocoaHttpServer (支持iOS、macOS及多種網絡場景)
GCDWebServer (基於iOS,不支持 https 及 webSocket)
Telegraph (Swift實現,功能較上面兩類更完善)
複製代碼
//一樣是因爲進程間通訊性能問題,HTTPBody字段被丟棄
[request setHTTPMethod:@"POST"];
[request setHTTPBody:[@"bodyData" dataUsingEncoding:NSUTF8StringEncoding]];
[wkwebview loadRequest: request];
複製代碼
解決:假如想經過-[WKWebView loadRequest:]加載 post 請求 (原始請求)request1: h5.nanhua.com/order/list,能夠經過如下步驟實現: