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的更多相关文章

  1. vue 如何在循环中 "监听" 的绑定v-model数据

    vue 如何在循环中 "监听" 的绑定v-model数据 阅读目录 vue 如何在循环中 "监听" 的绑定v-model数据 1. 普通属性的值进行监听 2. ...

  2. iOS监听模式系列之关于delegate(代理,委托)的学习

    首先,大家应该都明白的是委托是协议的一种,顾名思义,就是委托他人帮自己去做什么事.也就是当自己做什么事情不方便的时候,就可以建立一个委托,这样就可以委托他人帮自己去实现什么方法. 其次,我简单的总结了 ...

  3. 监听localStorage中的数据变化

    问题描述:我们在js里面获取了某一个localstorage的值,但是后期它可能改变了,我们js只执行一遍没办法再次获取它的值,当然可以刷新页面获取,但如果是我们的但页面就不能刷新页面了,此时:我们可 ...

  4. iOS监听模式系列之通知中心

    补充--通知中心 对于很多初学者往往会把iOS中的本地通知.推送通知和iOS通知中心的概念弄混.其实二者之间并没有任何关系,事实上它们都不属于一个框架,前者属于UIKit框架,后者属于Foundati ...

  5. iOS监听模式系列之键值编码KVC、键值监听KVO的简单介绍和应用

    键值编码KVC 我们知道在C#中可以通过反射读写一个对象的属性,有时候这种方式特别方便,因为你可以利用字符串的方式去动态控制一个对象.其实由于ObjC的语言特性,你根部不必进行任何操作就可以进行属性的 ...

  6. angular 使用rxjs 监听同级兄弟组件数据变化

    angular 的官网给出了父子组件之间数据交互的方法,如ViewChild.EventEmitter 但是如果要在同级组件之间进行数据同步,似乎并没有给出太多的信息. 有时候我们想,在一个组件中修改 ...

  7. iOS 监听控件某个属性的改变observeValueForKeyPath

    创建一个测试的UIButton #import "ViewController.h" @interface ViewController () @property(nonatomi ...

  8. iOS监听tableView组头切换事件

    - (void)tableView:(UITableView *)tableView willDisplayHeaderView:(UIView *)view forSection:(NSIntege ...

  9. IOS监听屏幕状态

    一.定义两个宏   //锁屏通知 #define NotificationOff CFSTR("com.apple.springboard.lockcomplete")   //解 ...

随机推荐

  1. Delphi url 编码及转码及特殊字符串替换--百度和腾讯用的就是这个

    先介绍一下,Delphi中处理Google的URL编码解码,其中就会明白URL编码转换的方法的 从delphi的角度看Google(谷歌)URL编码解码方式 在网上搜索了一下,似乎没有什么关于goog ...

  2. Codility---FrogRiverOne

    Task description A small frog wants to get to the other side of a river. The frog is initially locat ...

  3. Python基础,day3

    本节内容 1. 函数基本语法及特性 2. 参数与局部变量 3. 返回值 嵌套函数 4.递归 5.匿名函数 6.函数式编程介绍 7.高阶函数 8.内置函数 1.函数基本语法及特性 如何不重复代码,其实很 ...

  4. 【转载】Spring Boot引起的“堆外内存泄漏”排查及经验总结

    背景 为了更好地实现对项目的管理,我们将组内一个项目迁移到MDP框架(基于Spring Boot),随后我们就发现系统会频繁报出Swap区域使用量过高的异常.笔者被叫去帮忙查看原因,发现配置了4G堆内 ...

  5. 5分钟学会Java9-Java11的七大新特性

    现在Java有多元化的发展趋势,既有JS又有C++还有C#的影子,不学习那是不行滴. 来来来,花5分钟看看Java9-Java11的七大新特性,还有代码样例. Java11 发布了,然而很多公司还在用 ...

  6. 系统学习 Java IO (十三)----字符读写 Reader/Writer 及其常用子类

    目录:系统学习 Java IO---- 目录,概览 Reader Reader 类是 Java IO API 中所有 Reader 子类的基类. Reader 类似于 InputStream ,除了它 ...

  7. JSP数据交互(二)

    1.application内置对象 application实现用户之间的数据共享 void setAttribute(String key,Object value) 以key/value的形式保存对 ...

  8. denied: requested access to the resource is denied

    1.vim  /etc/docker/daemon.json    增加一个daemon.json文件 { "insecure-registries":["192.168 ...

  9. ECS通过mail发送邮件

    发送邮件报错,因为新购实例默认对外访问25端口封禁状态,建议使用加密465端口 1.配置mailx [root@wiki ~]# yum install -y mailx ##yum安装完mailx之 ...

  10. BZOJ 1086:[SCOI2005]王室联邦(DFS树分块)

    http://www.lydsy.com/JudgeOnline/problem.php?id=1086 题意:给出n个点的树,让你对树进行分块,每块的大小范围在[b, 3b]之间. 思路:一开始想着 ...