WKWebView 的性能优化

WKWebView 的性能优化

起因

随着移动设备性能不断增强,web 页面的性能体验逐渐变得可以接受,又因为 web 开发模式的诸多好处(跨平台,动态更新,减体积,无限扩展),APP 客户端里出现越来越多内嵌 web 页面),很多 APP 把一些功能模块改成用 H5 实现。

虽然说 H5 页面性能变好了,但如果没针对性地做一些优化,体验还是很糟糕的,主要两部分体验:

  1. 页面启动白屏时间:打开一个 H5 页面需要做一系列处理,会有一段白屏时间,体验糟糕。
  2. 响应流畅度:由于 webkit 的渲染机制,单线程,历史包袱等原因,页面刷新/交互的性能体验不如原生。

由于以上原因,公司准备从第一点入手,做 webview 的优化项目(达到秒开 webview )。因为UIWebView在 iOS12 就被标记废弃了,所以决定先从WKWebView入手研究。

思路

webview 加载过程

webview展示流程.png

打开一个页面的过程有很多优化点,包括前端和客户端,常规的前端和后端的性能优化已有前辈们总结过最佳实践,主要的是:

  • 降低请求量:合并资源,减少 HTTP 请求数,minify / gzip 压缩,webP,lazyLoad。
  • 加快请求速度:预解析DNS,减少域名数,并行加载,CDN 分发。
  • 缓存:HTTP 协议缓存请求,离线缓存 manifest,离线数据缓存 localStorage。
  • 渲染:JS/CSS优化,加载顺序,服务端渲染模板直出。
  • 客户端:预请求web所需数据
    大家可以看出,主要是第三阶段之前,用户看到的页面一直处于白屏。首先要优化的就是这段时间。
    打开一个页面的过程有很多优化点,包括前端和客户端,常规的前端和后端的性能优化已有前辈们总结过最佳实践,主要的是:

下面只讲客户端优化部分:

减少第一阶段耗时

  1. 在使用前预先初始化好 webView,从而减小耗时。
  2. 在初始化的同时,通过 Native 来完成一些网络请求等过程,使得 webView 初始化不是完全的阻塞后续过程。
  3. webview 池,可以用两个或多个 webview 重复使用,而不是每次打开 H5 都新建 webview。

减少第二阶段耗时

  1. 离线包
    1. 预先下载离线包,可以达到立即展示的效果。
    2. 离线包可以很方便地根据版本做增量更新。
    3. 离线包以压缩包的方式下发,同时会经过加密和校验,防止运营商和第三方对其劫持篡改。
  2. 数据缓存
    1. 第一次打开会有延迟,但是后续打开就会很快
    2. 可以自己控制缓存,方便管理
  3. 客户端代替请求
    1. 客户端可以在网络请求上做像 DNS 预解析/ IP 直连/长连接/并行请求等更细致的优化

难点

方案是通用的,不区分 UIWebView 和 WKWebView,但是目前很少有以 WKWebView 为目标的方案,那么以上技术方案在 WKWebView 中实现有什么难点呢?
难点在 NSURLProtocol

WKWebView 无法使用 NSURLProtocol 拦截 http 请求

这个问题网上早有方案:
[WKBrowsingContextController registerSchemeForCustomProtocol:@"schemes"];

WKWebView 使用 NSURLProtocol 拦截后,HTTPBody的数据会丢失

从网上克隆了 webkit 进行编译调试,尝试解决 Body 丢失的问题(体验到了啥叫大型项目的编译速度)


image.png

从图上重点标注的地方可以看到:

  1. WKWebView 的网络请求是在另外一个进程中操作的,然后如果 app 主进程需要拦截请求的话,通过 XPC 来进行两个进程间的通信。

  2. 苹果出于性能或其他考虑,会在给主进程的 URLProtocol 传输请求时将 HTTPBodyHTTPBodyStream 置为 nil 。
    源代码

尝试解决方案:
  1. 使用 runtime 黑魔法,在其将 HTTPBody 置为 nil 之前,先保存下来?
    因为网络请求是在其他进程中操作,没有办法在主进程使用 runtime 进行拦截。也就是说在 app 中决定拦截 http 请求的那一刻起,拦截到的请求注定是没有 HTTPBody 的。
  2. 使用 任何方式进入到 Networking 进程做一些操作 ?
    尝试了 Mac 端的 XPC demo,XPC 的回调是在各自进程,是不能操作其他进程的。
  3. HTTPBody 置为 nil 之前,是否会有代码走到主进程,然后拿到 request 进行操作?
    抱歉,经过测试,在 HTTPBody 置为 nil 之前,主进程不会收到关于 request 的调用
xpc 的 demo

解决

难到就没有任何方法解决了么?无意中看到一个特别有趣的想法又点燃了我的希望。

既然 Networking 进程会将 HTTPBody 置为 nil ,那我要做的就是两点:
1. 不让其置为 nil
2. 或者在其置为 nil 之前,先将 HTTPBody 保存下来

第一点:上面已经尝试失败;第二点:在 native 端也尝试失败,那在 H5 侧做保存操作呢?
要拦截的是 H5 的请求,那说明 H5 侧肯定是知道请求参数的。

尝试 H5 与 native 结合来解决 HTTPBody 丢失问题

H5 发起发起请求有三种方式:
1. Form
2. XMLHttpRequest
3. Fetch

Fetch是在 iOS10 以后支持的,从通用场景看,只需要处理 FormXMLHttpRequest 发起的带 HTTPBody 的请求就可以.

基本所有 H5 开发者,肯定知道 H5 里面也有黑魔法,就是原型:
XMLHttpRequest 对应的是 XMLHttpRequest.prototype.send 方法
Form 对应的是 HTMLFormElement.prototype.submit 方法

我们对以上方法使用 WKUserScriptWKUserScriptInjectionTimeAtDocumentStart 时机做对应拦截。这样 H5 在发起请求前,先将 POST 的数据发送给 native 存储(WKScriptMessageHandler)。然后在 native 拦截到匹配到的请求,尝试接管,并重新设置 HTTPBody,而且由于拦截到的是 request ,只需要补齐HTTPBody,其他在 h5 中原本对 request 做的各种操作也是存在的,这样就能解决问题了

这个方法提供了一种解决HTTPBody 丢失问题的可能,并且大部分 app,使用应该完全够用。本人已经按照上述方案实现,并接入到 app 中,在解决了一些细节问题后,将各个流程中的 H5 页面走了一遍,目前没有发现不支持的请求。


感兴趣的可以自己下载编译 webkit
XPCDemo


参考:
WebView性能、体验分析与优化
移动 H5 首屏秒开优化方案探讨
IMYWebLoader
VasSonic

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342

推荐阅读更多精彩内容