iOS8開始蘋果推薦使用WKWebview做爲H5開發的核心組件,以替代原有的UIWebView,如下是webkit基本介紹介紹: 介紹博客 Webkit 前端
window.performance 是W3C性能小組引入的新的API,主流瀏覽器都支持 iOS能夠獲取的字段能夠經過xcode官方文檔查看:(WebKit JS
只有作Safari編程才能使用,因此只能查看) web
W3C的Performance的時間先後順序以下: 編程
屬性說明:json
navigationStart:瀏覽器處理當前網頁的啓動時間 fetchStart:瀏覽器發起http請求讀取文檔的毫秒時間戳。 domainLookupStart:域名查詢開始時的時間戳。 domainLookupEnd:域名查詢結束時的時間戳。 connectStart:http請求開始向服務器發送的時間戳。 connectEnd:瀏覽器與服務器鏈接創建(握手和認證過程結束)的毫秒時間戳。 requestStart:瀏覽器向服務器發出http請求時的時間戳。或者開始讀取本地緩存時。 responseStart:瀏覽器從服務器(或讀取本地緩存)收到第一個字節時的時間戳。 responseEnd:瀏覽器從服務器收到最後一個字節時的毫秒時間戳。 domLoading:瀏覽器開始解析網頁DOM結構的時間。 domInteractive:網頁dom樹建立完成,開始加載內嵌資源的時間。 domContentLoadedEventStart:網頁DOMContentLoaded事件發生時的時間戳。 domContentLoadedEventEnd:網頁全部須要執行的腳本執行完成時的時間,domReady的時間。 domComplete:網頁dom結構生成時的時間戳。 loadEventStart:當前網頁load事件的回調函數開始執行的時間戳。 loadEventEnd:當前網頁load事件的回調函數結束運行時的時間戳。xcode
直接上代碼: 經過在wkwebview的didFinish方法中使用自定義的jsTiming方法:瀏覽器
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) { webView.jsTiming() }
import WebKit protocol MyWebViewTimingProtocal { func jsTiming() } extension WKWebView : MyWebViewTimingProtocal{ /// 獲取WebView的JS的性能數據 func jsTiming() { let webView : WKWebView? = self if #available(iOS 10.0, *) { webView?.evaluateJavaScript("JSON.stringify(window.performance.timing.toJSON())") { (timingStr, error) in if error == nil && timingStr != nil { JSTimingTool.parseJSTimingString(timingStr as! String) } else { print("WKWebView Load Performance JS Faild!") } } } else { let jsFuncStr = "function flatten(obj) {" + "var ret = {}; " + "for (var i in obj) { " + "ret[i] = obj[i];" + "}" + "return ret;}" webView?.evaluateJavaScript(jsFuncStr) { (resultStr, error) in if error == nil && resultStr != nil { webView?.evaluateJavaScript("JSON.stringify(flatten(window.performance.timing))", completionHandler: { (timingStr, error) in if error == nil && timingStr != nil { JSTimingTool.parseJSTimingString(timingStr as! String) } else { print("WKWebView Load Performance JS Faild!") } }) } else { print("WKWebView evaluateJavaScript Faild!") } } } } } /// 解析window.performance的工具類 private class JSTimingTool { /// 解析入口方法 /// /// - Parameter timingStr:window.performance.timing字符串 static func parseJSTimingString(_ timingStr: String) { if let dict = JSTimingTool.dictionaryFromString(timingStr) { JSTimingTool.parseJSTimingDictionary(dict) } else { print("Performance JS trans to Dictionary Faild!") } } /// 字符串轉字典 /// /// - Parameter str: 須要轉換的字符串 /// - Returns: 轉換完成的字典 static func dictionaryFromString(_ str: String) -> [String : Any]?{ let data = str.data(using: String.Encoding.utf8) if let dict = try? JSONSerialization.jsonObject(with: data!, options: JSONSerialization.ReadingOptions.mutableContainers) as? [String : Any] { return dict } return nil } /// 分析性能數據字典 /// /// - Parameter dict: window.performance.timing字典 static func parseJSTimingDictionary(_ dict: Dictionary<String, Any>) { print("\(String(describing: dict))") let domainLookupStart = dict["domainLookupStart"] as! CLongLong let domainLookupEnd = dict["domainLookupEnd"] as! CLongLong let connectStart = dict["connectStart"] as! CLongLong let connectEnd = dict["connectEnd"] as! CLongLong let responseStart = dict["responseStart"] as! CLongLong let responseEnd = dict["responseEnd"] as! CLongLong let domInteractive = dict["domInteractive"] as! CLongLong let domComplete = dict["domComplete"] as! CLongLong let fetchStart = dict["fetchStart"] as! CLongLong let domLoading = dict["domLoading"] as! CLongLong let domContentLoadedEventEnd = dict["domContentLoadedEventEnd"] as! CLongLong let loadEventStart = dict["loadEventStart"] as! CLongLong let loadEventEnd = dict["loadEventEnd"] as! CLongLong let dnstiming = domainLookupEnd - domainLookupStart //DNS查詢耗時 let tcptiming = connectEnd - connectStart //TCP連接耗時 let requesttiming = responseEnd - responseStart //request請求耗時 let domtiming = domComplete - domInteractive //解析dom樹耗時 let wheetScreentiming = domLoading - fetchStart //白屏時間 let domreadytiming = domContentLoadedEventEnd - fetchStart //dom ready時間 let domloadtiming = loadEventEnd - loadEventStart //dom load時間 let onloadtiming = loadEventEnd - fetchStart //onload總時間 print("dnstiming:\(dnstiming)\ntcptiming:\(tcptiming)\nrequesttiming:\(requesttiming)\ndomtiming:\(domtiming)\nwheetScreentiming:\(wheetScreentiming)\ndomreadytiming:\(domreadytiming)\ndomloadtiming:\(domloadtiming)\nonloadtiming:\(onloadtiming)\n") } }
以http://www.baidu.com爲例獲取到的數據緩存
["navigationStart": 1563415353543, "connectStart": 1563415353858, "redirectStart": 0, "unloadEventEnd": 0, "loadEventStart": 1563415358406, "responseEnd": 1563415354271, "domainLookupEnd": 1563415353857, "redirectEnd": 0, "connectEnd": 1563415353921, "secureConnectionStart": 1563415353888, "unloadEventStart": 0, "domContentLoadedEventStart": 1563415354271, "responseStart": 1563415354218, "loadEventEnd": 1563415358406, "domInteractive": 1563415354271, "requestStart": 1563415353921, "domComplete": 1563415358406, "domLoading": 1563415354231, "fetchStart": 1563415353852, "domContentLoadedEventEnd": 1563415354271, "domainLookupStart": 1563415353855] dnstiming:2 tcptiming:63 requesttiming:53 domtiming:4135 wheetScreentiming:379 domreadytiming:419 domloadtiming:0 onloadtiming:4554
若是須要針對全部頁面都監控,可使用runtime機制,監聽webview的didFinish方法,經過AOP方式hook到對應的自定義didFinish方法,而後在自定義的didFinish方法中調用jsTiming方法服務器
window.performance只能在webview的didFinish方法中監聽一次,若是H5頁面內部作跳轉,是沒法監聽到的,因此更適合作首次加載的性能分析,若是有二級H5頁面的的性能監聽需求,仍是須要前端開發同窗進行協助。dom