iOS-监听原生H5性能数据window.performance
WebKit-WKWebView
iOS8开始苹果推荐使用WKWebview作为H5开发的核心组件,以替代原有的UIWebView,以下是webkit基本介绍介绍:
介绍博客 Webkit
H5 - window.performance
window.performance 是W3C性能小组引入的新的API,主流浏览器都支持
iOS可以获取的字段可以通过xcode官方文档查看:(WebKit JS
只有做Safari编程才能使用,所以只能查看)
W3C的Performance的时间前后顺序如下:
属性说明:
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事件的回调函数结束运行时的时间戳。
通过代码获取数据
直接上代码:
通过在wkwebview的didFinish方法中使用自定义的jsTiming方法:
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
webView.jsTiming()
}
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页面的的性能监听需求,还是需要前端开发同学进行协助。
iOS-监听原生H5性能数据window.performance的更多相关文章
- vue 如何在循环中 "监听" 的绑定v-model数据
vue 如何在循环中 "监听" 的绑定v-model数据 阅读目录 vue 如何在循环中 "监听" 的绑定v-model数据 1. 普通属性的值进行监听 2. ...
- iOS监听模式系列之关于delegate(代理,委托)的学习
首先,大家应该都明白的是委托是协议的一种,顾名思义,就是委托他人帮自己去做什么事.也就是当自己做什么事情不方便的时候,就可以建立一个委托,这样就可以委托他人帮自己去实现什么方法. 其次,我简单的总结了 ...
- 监听localStorage中的数据变化
问题描述:我们在js里面获取了某一个localstorage的值,但是后期它可能改变了,我们js只执行一遍没办法再次获取它的值,当然可以刷新页面获取,但如果是我们的但页面就不能刷新页面了,此时:我们可 ...
- iOS监听模式系列之通知中心
补充--通知中心 对于很多初学者往往会把iOS中的本地通知.推送通知和iOS通知中心的概念弄混.其实二者之间并没有任何关系,事实上它们都不属于一个框架,前者属于UIKit框架,后者属于Foundati ...
- iOS监听模式系列之键值编码KVC、键值监听KVO的简单介绍和应用
键值编码KVC 我们知道在C#中可以通过反射读写一个对象的属性,有时候这种方式特别方便,因为你可以利用字符串的方式去动态控制一个对象.其实由于ObjC的语言特性,你根部不必进行任何操作就可以进行属性的 ...
- angular 使用rxjs 监听同级兄弟组件数据变化
angular 的官网给出了父子组件之间数据交互的方法,如ViewChild.EventEmitter 但是如果要在同级组件之间进行数据同步,似乎并没有给出太多的信息. 有时候我们想,在一个组件中修改 ...
- iOS 监听控件某个属性的改变observeValueForKeyPath
创建一个测试的UIButton #import "ViewController.h" @interface ViewController () @property(nonatomi ...
- iOS监听tableView组头切换事件
- (void)tableView:(UITableView *)tableView willDisplayHeaderView:(UIView *)view forSection:(NSIntege ...
- IOS监听屏幕状态
一.定义两个宏 //锁屏通知 #define NotificationOff CFSTR("com.apple.springboard.lockcomplete") //解 ...
随机推荐
- Spring Boot配置篇(基于Spring Boot 2.0系列)
1:概述 SpringBoot支持外部化配置,配置文件格式如下所示: properties files yaml files environment variables command-line ar ...
- Python连载7-time包的其他函数
接连载6 一.time包 1.函数:sleep(second) (1)含义:是程序进入休眠状态多少秒 (2)格式:time.sleep(int num) 2.函数:strftime() (1)含义:将 ...
- 高并发 Nginx+Lua OpenResty系列(3)——模块指令
Nginx Lua 模块指令 Nginx共11个处理阶段,而相应的处理阶段是可以做插入式处理,即可插拔式架构:另外指令可以在http.server.server if.location.locatio ...
- Linux下Flume的安装部署
一.前置条件 Flume需要依赖JDK 1.8+,JDK安装方式见本仓库: Linux环境下JDK安装 二 .安装步骤 2.1 下载并解压 下载所需版本的Flume,这里我下载的是CDH版本的Flum ...
- Mint-ui全局引入
1.Mint-ui在全局引入之后,在组件中使用其中的js函数,如toast() this.$toast('在全局引入之后可以直接在this.$toast中使用')
- appcan 多按钮提示框
使用 appcan.window.alert EG: var btnList=new Array(); btnList[0]="确认"; btnList[1]="取消& ...
- Python编程菜鸟成长记--A1--04--Hello World!
1.重点知识 掌握使用 命令行.文件.Jupyter 的方式执行 Python 代码 2.Hello World! 自从 C 语言之父 丹尼斯.M.里奇 在<The C Programming ...
- Qt实现表格控件-支持多级列表头、多级行表头、单元格合并、字体设置等
目录 一.概述 二.效果展示 三.定制表头 1.重写数据源 2.重写QHeaderView 四.设置属性 五.相关文章 原文链接:Qt实现表格控件-支持多级列表头.多级行表头.单元格合并.字体设置等 ...
- java word转html 报错 org/apache/poi/xwpf/usermodel/IRunBody
最终解决的办法是修改jar包版本,一定要对应上. <dependency> <groupId>org.apache.poi</groupId> <artifa ...
- 【linux杂谈】在SSH连接中,openssh如何解决'Connection refused'错误?
openssh是SSH (Secure SHell) 协议的免费开源实现.SSH协议族可以用来进行远程控制, 或在计算机之间传送文件. 这就意味着远程登陆,文件推拉特别是搭建集群后公钥的部署,经常要利 ...