WebView性能优化总结

时间:2022-04-20 12:44:56

转载https://www.cnblogs.com/lys-iOS-study/p/7097774.html

WebView性能优化总结                                           

 

一个加载网页的过程中,native、网络、后端处理、CPU都会参与,各自都有必要的工作和依赖关系;让他们相互并行处理而不是相互阻塞才可以让网页加载更快:

·     WebView初始化慢,可以在初始化同时先请求数据,让后端和网络不要闲着。

·     后端处理慢,可以让服务器分trunk输出,在后端计算的同时前端也加载网络静态资源。

·     脚本执行慢,就让脚本在最后运行,不阻塞页面解析。

·     同时,合理的预加载、预缓存可以让加载速度的瓶颈更小。

·     WebView初始化慢,就随时初始化好一个WebView待用。

·     DNS和链接慢,想办法复用客户端使用的域名和链接。

·     脚本执行慢,可以把框架代码拆分出来,在请求页面之前就执行好。

 

WebView内存消耗

 

分析

 

为了测试WebView会消耗多少内存,我们设计了如下的测试方案:

1.       客户端启动后,记录消耗的内存。

2.       打开空页面,记录内存的上涨。

3.       退出。

4.       打开空页面,记录内存上涨。

5.       退出。

6.       打开加载了代码的页面,记录内存的额外增加。

得到如下测试结果:

 

测试系统: iOS模拟器,Titans 10.0.7

测试系统: OPPO R829T Android 4.2.2

测试方式:测试10次取平均值

 

 

首次打开增加内存

二次打开增加内存

加载KNB+VUE+灵犀

iOS UIWebView

31.1M

5.52M

2M

iOS WKWebView

1.95M

1.6M

2M

Android

32.2M

6.62M

1.7M

 

 

WKWebView的内存消耗相比其他低了一个数量级,在此方面相当占优。

 

UIWebViewAndroidWebView在首次初始化时都要消耗大量内存,之后每次新建WebView会额外增加一些。

 

UIWebView的内存占用不会在关闭WebView时主动回收,每次新开WebView都会消耗额外内存。

 

相比于性能,对于内存的优化可以做的还是比较有限的。

·     WKWebView的内存占用优势比较大(代价是初始化比较慢)。

·     页面内代码消耗的内存相比与WebView系统的内存消耗相比可以说是很低。

 

WebView体验

 

除了打开的速度,WebView通常体验也没有native的实现更好,我们可以找到以下几个例子:

 

长按选择

 

WebView中,长按文字会使得WebView默认开始选择文字;长按链接会弹出提示是否在新页面打开。

解决方法:可以通过给body增加CSS来禁止这些默认规则。

 

点击延迟

 

WebView中,click通常会有大约300ms的延迟(同时包括链接的点击,表单的提交,控件的交互等任何用户点击行为)。

 

唯一的例外是设置的metaviewpoint为禁止缩放的Chrome(然而并不是Android默认的浏览器)。

 

解决方法:使用fastclick一般可以解决这个问题。

 

页面滑动期间不渲染/执行

 

在很多需求中会有一些吸顶的元素,例如导航条,购买按钮等;当页面滚动超出元素高度后,元素吸附在屏幕顶部。

 

这个功能在PCnative中都能够实现,然而在WebView中却成了难题:

 在页面滚动期间,Scroll Event不触发

 

不仅如此,WebView在滚动期间还有各种限定:

·     setTimeoutsetInterval不触发。

·     GIF动画不播放。

·     很多回调会延迟到页面停止滚动之后。

·     background-position:fixed不支持。

·     这些限制让WebView在滚动期间很难有较好的体验。

 

这些限制大部分是不可突破的,但至少对于吸顶功能还是可以做一些支持:

 

解决方法:

·     iOS上,使用position:sticky可以做到元素吸顶。

·     Android上,监听touchmove事件可以在滑动期间做元素的position切换(惯性运动期间就无效了)。

 

键盘形态有限

 

WebView对键盘的控制能力很弱,无法直接调起或者隐藏键盘,而且键盘的确认文案是无法自定义的。

我们以百度为例:

 WebView性能优化总结

 

当你打开百度搜索时,点击【换行】就完成了输入并开始了搜索。

 

为什么是【换行】而不是【搜索】呢?

 

当然不是bug……而是……臣妾做不到啊!

 

解决方法:

 

目前只能通过由与App通过桥协议的方式,由App代为唤起键盘(但是实际操作过于复杂)。

 

crash

 

通常WebView并不能直接接触到底层的API,因此比较稳定;但仍然有使用不当造成整个App崩溃的情况。

目前发现的案例包括:

·     使用过大的图片(2M

·     不正常使用WebGL

 

WebView安全

 

WebView被运营商劫持、注入问题

 

由于WebView加载的页面代码是从服务器动态获取的,这些代码将会很容易被中间环节所窃取或者修改,其中最主要的问题出自地方运营商(浙江尤其明显)和一些WiFi

 

我们监测到的问题包括:

·      无视通信规则强制缓存页面。

·      header被篡改。

·      页面被注入广告。

·      页面被重定向。

·      页面被重定向并重新iframe到新页面,框架嵌入广告。

·      HTTPS请求被拦截。

·      DNS劫持。

 

这些问题轻则影响用户体验,重则泄露数据,或影响公司信誉。

 

针对页面注入的行为,有一些解决方案:

 
使用CSPContent Security Policy

 

CSP可以有效的拦截页面中的非白名单资源,而且兼容性较好。在美团移动版的使用中,能够阻止大部分的页面内容注入。

 

但在使用中还是存在以下问题:

·      由于业务的需要,通常inline脚本还是在白名单中,会导致完全依赖内联的页面代码注入可以通过检测。

·      如果注入的内容是纯HTML+CSS的内容,则CSP无能为力。

·      无法解决页面被劫持的问题。

·      会带来额外的一些维护成本。

 

总体来说CSP是一个行之有效的防注入方案,但是如果对于安全要求更高的网站,这些还不够。

 

HTTPS

 

HTTPS可以防止页面被劫持或者注入,然而其副作用也是明显的,网络传输的性能和成功率都会下降,而且HTTPS的页面会要求页面内所有引用的资源也是HTTPS的,对于大型网站其迁移成本并不算低。

 

HTTPS的一个问题在于:一旦底层想要篡改或者劫持,会导致整个链接失效,页面无法展示。这会带来一个问题:本来页面只是会被注入广告,而且广告会被CSP拦截,而采用了HTTPS后,整个网页由于受到劫持完全无法展示。

 

对于安全要求不高的静态页面,就需要权衡HTTPS带来的利与弊了。

 

App使用Socket代理请求

 

如果HTTP请求容易被拦截,那么让App将其转换为一个Socket请求,并代理WebView的访问也是一个办法。

 

通常不法运营商或者WiFi都只能拦截HTTPS)请求,对于自定义的包内容则无法拦截,因此可以基本解决注入和劫持的问题。

 

Socket代理请求也存在问题。

·      首先,使用客户端代理的页面HTML请求将丧失边下载边解析的能力;根据前面所述,浏览器在HTML收到部分内容后就立刻开始解析,并加载解析出来的外链、图片等,执行内联的脚本……而目前WebView对外并没有暴露这种流式的HTML接口,只能由客户端完全下载好HTML后,注入到WebView中。因此其性能将会受到影响。

·      其次,其技术问题也是较多的,例如对跳转的处理,对缓存的处理,对CDN的处理等等……稍不留神就会埋下若干大坑。

 

此外还有一些其他的办法,例如页面的MD5检测,页面静态页打包下载等等方式,具体如何选择还要根据具体的场景抉择。

 

客户端内打开第三方WebView

 

一般来说,客户端内的WebView都是可以通过客户端的某个schema打开的,而要打开页面的URL很多都并不写在客户端内,而是可以由URL中的参数传递过去的。

 

那么,一旦此URL可以通过外界输入自定义,那么就有可能在客户端内部打开一个外部的网页。

 

例:作案过程

·      某个App有个WebView,打开的schemaappxx://web?url={weburl}

·      App中有个扫码的功能,可以扫描某个二维码并打开对应的schema链接。

·      某个坏人制作了一个二维码并张贴到街上,内容符合appxx://web?url={some_hack_weburl}

·      用户扫码打开了some_hack_weburl

·      如果some_hack_weburl是一个高仿的登录页面,那么用户将会很可能将用户名密码提交到其他网站。

 

解决方法:在内嵌的WebView中应该限制允许打开的WebView的域名,并设置运行访问的白名单。或者当用户打开外部链接前给用户强烈而明显的提示。

 

发展

 

在一个客户端内,native目前主要功能是提供高效而基础的功能;内部的WebView则添加一些性能体验要求不高但动态化要求高的能力。

 

提高客户端的动态能力,或者提高WebView的性能,都是提升App功能覆盖的方式。

 

而目前的各种框架,ReactNativeWeek包括微信小程序,都是这个趋势的尝试。

 

随着技术的发展,WebView的性能、体验和安全问题也将会逐渐的改善,在App中占有越来越多比重的同时,也将会为App开拓新的能力,为用户带来更优质的体验。