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. 解决C/C++程序执行一闪而过的方法(使用getchar,或者cin.get,不推荐system(“pause”))

    简述 在VS编写控制台程序的时候,包括使用其他IDE(Visual C++)编写C/C++程序,经常会看到程序的执行结果一闪而过,要解决这个问题,可以在代码的最后加上system(“pause”).g ...

  2. C++ 使用回调函数的方式 和 作用。 持续更新

    先看两个demo: 一.在类test1中调用函数print() ,把print()的函数指针传递给test1的函数指针参数 test1.h: #include <stdio.h> #inc ...

  3. YARN分析系列之三 -- 从脚本入口分析 ResourceManager的初始化过程

    1. 由脚本找到 RM 主类 这部分,我们从脚本作为入口去逐步深入ResourceManager源码. 从 Hadoop 官方文档 中可以看到 ResourceManager 的启动命令为: Usag ...

  4. 快速理解类的访问控制(public,protected,private)

    接触过面向对象编程的朋友们都知道类中的访问控制符无非有三个,public, protected, private. 理解他就先从期望他达到的效果先说吧 Public: 使成员对于整个程序内(类内类外) ...

  5. Django 的路由系统

    Django 的路由系统   Django 的路由系统 路由层 urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^$',views.ho ...

  6. MyBatis从入门到精通(二):MyBatis XML方式的基本用法之Select

    最近在读刘增辉老师所著的<MyBatis从入门到精通>一书,很有收获,于是将自己学习的过程以博客形式输出,如有错误,欢迎指正,如帮助到你,不胜荣幸! 1. 明确需求 书中提到的需求是一个基 ...

  7. idea初见问题整理_错误: -source 1.5 中不支持 diamond 运算符

    最近在移动工程到idea下,顺便改目录结构,遇到的问题不一定全部记录,有些答案摘抄自别人博客,已注明来源,由于不是摘抄自同一作者,且有自己的一些内容,所以标注为原创. 1.(错误: -source 1 ...

  8. php7中异常

    php7中新增异常错误处理 在PHP7之前的版本,对于一些错误异常是没有办法捕获的. php7中新增throwable接口,可以用来捕获一些错误 Exception,Error这实现了Throwabl ...

  9. laravel-admin(自定义表单视图)

    前言: 在上一遍文章(https://www.cnblogs.com/shiwenhu/p/10271013.html)中写到可以使用自定义form组建来创建表单,几乎能满足我们大部分要求,而且不用我 ...

  10. javascript函数详解

    //函数的两种声明方式 //在同一个<script>标签中,函数的调用和声明位置可以没有先后的顺序,因为在同一个标签中,都是等加载到内存中,然后在运行 //但是如果是在两个script标枪 ...