移動端本地 H5 秒開
一、為什麼 H5 體驗糟糕
為什麼開啟一個 H5 頁面會有一長段白屏時間?因為它做了很多事情,大概是:
初始化 webview -> 請求頁面 -> 下載資料 -> 解析HTML -> 請求 js/css 資源 -> dom 渲染 -> 解析 JS 執行 -> JS 請求資料 -> 解析渲染 -> 下載渲染圖片
一般頁面在 dom 渲染後才能展示,可以發現,H5 首屏渲染白屏問題的原因關鍵在於,如何優化減少從請求下載頁面到渲染之間這段時間的耗時。
二、如何優化
上述開啟一個頁面的過程有很多優化點,包括前端和客戶端,常規的前端和後端的效能優化已有前輩們總結過最佳實踐,主要的是:
降低請求量:合併資源,減少 HTTP 請求數,minify / gzip 壓縮,webP,lazyLoad。
加快請求速度:預解析DNS,減少域名數,並行載入,CDN 分發。
快取:HTTP 協議快取請求,離線快取 manifest,離線資料快取 localStorage。
渲染:JS/CSS優化,載入順序,服務端渲染模板直出。
一般情況下,我們只要對照這個列表,對比差異就基本能搞定絕大部分前端效能問題了。不過我們在裡面仔細再分析下,對首屏啟動速度影響最大的就是網路請求,包括請求 HTML、css、image 等靜態資源和展示資料的請求。
那麼將 H5 相關頁面和資源打包到客戶端中,然後客戶端將展示資料傳給頁面,通過webView載入展示
三、具體怎麼實現?
整體思路看起來比較清晰,但是其中有幾個關鍵問題需要解決:
3.1 本地H5頁面如何和native通訊:
本地 H5 頁面如何和 native 通訊的方式基本有三種:jsapi、URL Scheme 和 字串替換。具體不同方式適合使用場景有所不同:
jsapi :客戶端提供介面,注入 API讓 Javascript呼叫,直接執行相應Native程式碼,適用於需要通過互動,進行資料請求的場景
URL Scheme : Web 端傳送 URL Scheme 請求,之後 Native 攔截到請求並根據 URL Scheme 及所帶的引數進行相關操作。適用於進行頁面跳轉的場景。
字串替換: 客戶端讀取本地 H5後,通過對 H5 中的約定的標記位進行字串替換,然後載入展示頁面。適用於沒有複雜互動,只通過頁面渲染資料的場景。
3.2 如何開發除錯和維護
開發本地 H5 模組,很容易想到在本地通過模擬資料開發,然後將 H5 給到各客戶端打包後進行聯調。然而這樣的方案實現起來十分繁瑣,原因是 H5 資源給到客戶端打包時很分散,不統一,管理困難。
那麼我們改進一下,將使用本地 H5 實現模組的頁面建立一個統一 git 倉庫,IOS 和 android 客戶端通過git submodule
將本地 H5 的git 外鏈到專案中,這樣客戶端中的資源就可以統一管理,解放了每次都手動繁瑣的替換打包工作。
但是這種方法其實也並不完美,H5 代替原生實現的優勢,一個在於開發成本低,另一個在於 H5 可以更加快捷的更新迭代,如果打包在客戶端中的H5 頁面就像客戶端一樣,沒法快速更新了。很容易想到將 H5 資源給到後臺,客戶端按照業務模組預下載整個離線包,離線包根據版本做增量更新。這種的方案,就可以較好的解決上面的問題了。
四、細節優化
解決了上面的問題,本地 H5 確實可以達到秒開的載入速度,不過要達到和客戶端一樣的體驗,還需要配上一些細節優化:
預載入 webView,預拉取資料
在聯調本地 H5 頁面過程中,發現首次載入頁面時間比後續開啟時間都慢很多,原因預計是 webView 首次初始化時候需要啟動資源和服務較多,於是嘗試客戶端在預先初始化 webView 方案,果然這樣第一次開啟頁面時候就變快了。同時為了 H5 在第一次開啟時能直接展示資料,客戶端在頁面開啟前就預拉取資料並快取,這樣來減少請求資料時間導致的白屏。
遮蔽webview HTML 內容自動識別
在 IOS webView 中預設會自動檢測 HTML 中手機號、email、地址格式並標記。
解決方法:通過新增 meta 頭來禁止預設行為
<meta name="format-detection" content="telephone=no,email=no,adress=no">
點選延遲
在WebView中,click通常會有大約300ms的延遲(同時包括連結的點選,表單的提交,控制元件的互動等任何使用者點選行為)。
解決方法:使用fastclick/touchend一般可以解決這個問題。
國際化
客戶端內的 H5 也需要國際化,前端國際化方案有很多,通常情況下都是根據專案框架選擇相應的國際化外掛,然而在本地 H5 的頁面中,再引入額外外掛會增加客戶端打包大小,略顯冗餘。適合自己的才是最好的,這裡採用了一種適合輕量級的國際化方案。
1.提取語言文案
2.頁面和 js 中引用提取的文案
3.根據配置切換語言方案
$('.i18n').each(function() {
var key = $(this).attr('name');
$(this).html(language[key]);
});
var language = getQueryVariable('en') ? i18n.en : i18n.zh
WKWebView 相容
WKWebView 效能比 UIViewView 效能好很多,所以客戶端開發一般都推薦使用 WKWebView。
但是使用 WKWebView 載入本地的 HTML 時也有一些相容問題,在 iOS8 不能在 HTML 檔案中引用本地的 css 或者 js 或者圖片檔案,IOS8 以上的是正常的,可以引用遠端資源。為了兼顧相容性和秒開體驗,所以做降級方案,通過系統版本動態載入JS, IOS8 使用網路資源,IOS8 以上使用本地資源。
還有在iOS8中,使用一些遠端的 cdn 的 css 或者 js 檔案,必須注意在引用標籤上加上 charset屬性,不然 css 和 js 庫將會亂碼
五、最後
從前端優化,到客戶端快取,到離線包,到更多的細節優化,做到上述這些點,H5 頁面在啟動上差不多可以媲美原生的體驗了。
總結起來,大體優化思路就是:減少一切網路請求,做好預載入和快取,儘量在使用者開啟之前就載入好所有內容。這裡有些優化手段也要根據專案和實際需求來評估,需要跟開發成本和效率權衡。上述討論的僅針對功能模組類的單頁面 H5 頁面秒開的優化方案,其他一些互動較複雜的 H5 頁面可能並不適用,還需要視實際情況和需求而定。
參考文獻
WebView效能、體驗分析與優化:https://tech.meituan.com/WebViewPerf.html
參考:https://mp.weixin.qq.com/s/0OR4HJQSDq7nEFUAaX1x5A