本文对比的是 UIWebView、WKWebView、flutter_webview_plugin(在 iOS 中使用的是 WKWebView)的加载速度,内存使用情况。

测试手机:iPhoneX

系统:iOS12.0

加载速度对比

测试网页打开的速度,只需要获取 WebView 在开始加载网页和网页加载完成时的时间戳,时间戳的差即为打开网页的时间。

WKWebView

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
extension WKWebViewVC: WKNavigationDelegate {

public func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
decisionHandler(.allow)
}

public func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {
startTime = Int(Date().timeIntervalSince1970 * 1000)
}

public func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
let finishTime: Int = Int(Date().timeIntervalSince1970 * 1000)
print("WKWebView \(finishTime - startTime)")
}
}

UIWebView

1
2
3
4
5
6
7
8
9
10
11
extension WebViewVC: UIWebViewDelegate {

public func webViewDidStartLoad(_ webView: UIWebView) {
startTime = Int(Date().timeIntervalSince1970 * 1000)
}

public func webViewDidFinishLoad(_ webView: UIWebView) {
let finishTime: Int = Int(Date().timeIntervalSince1970 * 1000)
print("UIWebView \(finishTime - startTime)")
}
}

flutter_webview_plugin

1
2
3
4
5
6
7
8
9
10
11
12
13
flutterWebViewPlugin.onStateChanged.listen((state) {
if (state.type == WebViewState.finishLoad) {
print('finishLoad:' + DateTime.now().millisecondsSinceEpoch.toString());
setState(() {
isLoad = false;
});
} else if (state.type == WebViewState.startLoad) {
print('startLoad:' + DateTime.now().millisecondsSinceEpoch.toString());
setState(() {
isLoad = true;
});
}
});

为了使差异更明显,我们选择较为复杂的 新浪首页 进行加载的对比,为了减小网络对加载速度的影响,我们让手机连接同一个网络,分别进行 10 次测试然后取平均值,另外,我们需要关闭 WebView 的缓存,防止缓存对加载速度产生影响:

1
2
3
4
5
6
private func delegateCache() {
let cache = URLCache.shared
cache.removeAllCachedResponses()
cache.diskCapacity = 0
cache.memoryCapacity = 0
}
1
2
3
4
5
6
7
private func deleteWebCache() {
let websiteDataTypes: Set<String> = WKWebsiteDataStore.allWebsiteDataTypes()
let dateFrom = Date.init(timeIntervalSince1970: 0)
WKWebsiteDataStore.default().removeData(ofTypes: websiteDataTypes, modifiedSince: dateFrom) {

}
}
1
2
3
4
5
6
7
8
9
WebviewScaffold(
key: _scaffoldKey,
url: widget.url,
clearCache: true,
appCacheEnabled: false,
.
.
.
);

下面使笔者进行 10 次测试所得到的数据:

UIWebView WKWebView flutter_webview_plugin
0 4009 3384 3582
1 2856 3719 2869
2 2773 3258 3221
3 2776 3570 3178
4 2933 3386 3092
5 2679 3706 2956
6 2583 3707 3038
7 3145 2947 3015
8 3654 3038 3634
9 3258 3439 3132
avg 3066.6 3415.4 3171.7

结果让我有点惊讶,一直以为 WKWebView 会是个王者。结果看,速度上 WKWebView 略慢一点,不过总体差异不大(该结果仅仅是测试新浪的结果,仅供参考啦)。

在这里,笔者又加了一个测试,尝试记录从 viewController 的 viewDidLoad 到 webview 的 didFinish 时间,测试了新浪的数据,如下:

UIWebViewA: 4970、3808、3815、4250、3556 avg(4079.8) (加载完所有页面)
UIWebViewB: 4103、3124、3070、3256、2835 avg(3277.6)(加载sina完毕)
WKWebView: 3672、3032、2892、2912、2739 avg(3049.4)
flutter_webView: 4532、3901、4310、3496、3378 avg(3923.4)

其中可以看到,webView 有两行,UIWebViewB 的数据就是加载 sina 主站的时间;UIWebViewA 的数据是因为在加载完 sina 主站之后,新浪又加载了一个https://r.dmp.sina.cn/cm/sinaads_ck_wap.html,所以导致总时间延长,不过即使按照 UIWebViewB 的数据来比较,也是 wkWebView 略胜一筹。

此处可以看出 flutter_webView 使用的是 wkwebView,所以它吃亏的主要原因是 flutter 包了一层。

结论:
速度(didStart -> didFinish) UIWebView > flutter_webview > WKWebView
速度(viewDidLoad -> didFinish)WKWebView > UIWebView > flutter_webview

占用内存对比

这里查看内存使用的是 Xcode 的 debug session 中的 memory,首先看之前测试时,连续打开十次新浪的内存情况:

UIWebView.png

WKWebView.png

flutter_webView.png

接着我们在看一下打开淘宝首页的内存情况

UIWebView.png WKWebView.png flutter_webView.png

从图上可以看出,WKWebView 在内存方面有很大的优势啊,UIWebView 的内存是真的伤啊,然后 debug 看了一下 flutter_webView,他使用的就是原生的 webView。

他相比较原生 WKWebView 的内存开销稍大一点,从测试表现来看,一般大个 30 MB 左右。

结论:内存 WKWebView > flutter_webview > UIWebView

HTML5 兼容性对比

可以在 html5test 中对浏览器的兼容性进行评分,通过测试发现得分分别如下:

UIWebView.png WKWebView.png flutter_webView.png

因为 flutter 里使用的就是 WK,所以和原生的 WKWebView 一样都是 444 分,比 UIWebView 的 437 略胜一筹。

结论:兼容性 WKWebView = flutter_webview > UIWebView

总结

  • UIWebView: 速度相比较 WKWebView 稍快一点,但是内存是一大硬伤,所以只要条件允许,就不推荐使用了;
  • WKWebView: 速度略慢一点,不过差别不大,总体可以接受。是比UIWebView更好的选择,推荐使用;
  • flutter_webView_plugin:在iOS中使用的就是原生的WKWebView,所以总体和 native WKWebView 表现差不多。如果是混编项目中,因为它被包了一层,所以页面加载上存在一定的劣势,所以混编项目中仍然推荐使用 WKWebView。不过如果从多端考虑、以及项目可迁移等,那么使用也未尝不可,就是维护成本要增加一些,需要维护两套 webView。这个就需要根据自己的情况自己取舍了。

再读一篇类似文章?推荐阅读姊妹篇:

Flutter 与 Android 的 WebView 对比


如有任何知识产权、版权问题或理论错误,还请指正。
https://xiangwushuo.github.io/posts/Flutter-iOS-WebView
本文作者 Jay,转载请注明原作者及以上信息。

评论和共享

前言

自从 google 推出 flutter 跨平台开发框架以来,flutter 在各个技术论坛里被炒得如日中天。

说到跨平台开发,就不得不提 WebView,WebView 可以说是最廉价的跨平台开发方案。我们知道,flutter 可以和 native 混合开发,它们可以互相调用。那么当我们进行混合开发的时候,如果需要使用 WebView,我们应该调用原生的 WebView 还是使用 flutter 自己实现 WebView 呢?如果用 flutter 自己实现 WebView,它的性能与 native 相比如何呢?今天我们就以 android 为例从几个不同的维度来实际测试一下!

Flutter 实现 WebView

flutter 官方是没有 WebView 组件的,不过强大的 flutter-community 论坛考虑到广大开发者的需求,开发了 flutter_webview_plugin 插件,方便在 flutter 中使用使用 WebView。

集成方式很简单,在 pubspec.yaml 文件中:

1
2
3
4
dependencies:
flutter:
sdk: flutter
flutter_webview_plugin: ^0.3.0+2

接下来所有的对比都是基于 Android 原生的 WebView 和 flutter_webview_plugin 插件,为了严谨,并未对第三方 WebView 作对比。

测试手机:小米8SE

系统:Android 8.1.0

加载速度对比

测试网页打开的速度,只需要获取 WebView 在开始加载网页和网页加载完成时的时间戳,时间戳的差即为打开网页的时间,我们分别在 Android 原生和 flutter 中的相应位置打印 log:

1
2
3
4
5
6
7
8
9
10
11
webView?.webViewClient = object : WebViewClient() {
override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) {
Log.d(TAG, "onPageStarted:" + System.currentTimeMillis())
super.onPageStarted(view, url, favicon)
}

override fun onPageFinished(view: WebView?, url: String?) {
Log.d(TAG, "onPageFinished:" + System.currentTimeMillis())
super.onPageFinished(view, url)
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
flutterWebViewPlugin.onStateChanged.listen((state) {
if (state.type == WebViewState.finishLoad) {
print('finishLoad:' + DateTime.now().millisecondsSinceEpoch.toString());
setState(() {
isLoad = false;
});
} else if (state.type == WebViewState.startLoad) {
print('startLoad:' + DateTime.now().millisecondsSinceEpoch.toString());
setState(() {
isLoad = true;
});
}
});

为了使差异更明显,我们选择较为复杂的 新浪首页 进行加载的对比,为了减小网络对加载速度的影响,我们让手机连接同一个网络,分别进行 10 次测试然后取平均值,另外,我们需要关闭 WebView 的缓存,防止缓存对加载速度产生影响:

1
webView?.settings?.cacheMode = WebSettings.LOAD_NO_CACHE
1
2
3
4
5
6
7
8
9
WebviewScaffold(
key: _scaffoldKey,
url: widget.url,
clearCache: true,
appCacheEnabled: false,
.
.
.
);

下面使笔者进行 10 次测试所得到的数据:

Android WebView flutter_webview_plugin
0 2731 2442
1 2502 2623
2 2747 2422
3 2652 2323
4 2553 2596
5 2645 2379
6 2670 2342
7 2691 2218
8 3773 2429
9 2631 2453
avg 2759.5 2421.7

可以发现,相同环境下 flutter_webview_plugin 的加载速度比 native WebView 略快,但是差异不明显,基本可以忽略。

结论:flutter_webview_plugin 的加载速度比 native WebView 略快。

内存占用对比

可以使用 AndroidStudio 自带的 profiler 工具来进行占用内存的测试,我们在 flutter 程序中同时集成调用 native WebView 和 flutter_webview_plugin 来打开淘宝首页和新浪首页的方法,在程序刚运行的时候内存占用如下图:

然后用 WebView 打开淘宝首页:

用 flutter_webview_plugin 打开淘宝首页:

可以发现,用 WebView 打开淘宝首页内存基本无变化,但是用 flutter_webview_plugin 打开淘宝首页内存有明显的增加,且波动较大。

结论:flutter_webview_plugin 相对 native WebView 而言,占用内存较大。

HTML5 兼容性对比

可以在 html5test 中对浏览器的兼容性进行评分,通过测试发现 native WebView 和 flutter_webview_plugin 的得分分别如下:

发现在小米8SE手机上,native WebView 和 flutter_webview_plugin 的 html5 兼容性得分都是 501。

结论:native WebView 和 flutter_webview_plugin 的 html5 兼容性无明显差异。

总结

我们对 native WebView 和 flutter_webview_plugin 分别进行了网页加载速度、占用内存和 html5 兼容性作了对比,发现 native WebView 占用内存更小,网页加载速度和 html5 的兼容性无明显差异。

在实际使用中,由于 flutter_webview_plugin 并不存在于 widget 树中,所以不能在 flutter_webview_plugin 中使用如 snackbars, dialogs…这些通知交互 widget。但是 flutter_webview_plugin 具有跨平台的优势,如果需要同时 flutter 项目中同时在 Android 和 iOS 端使用 WebView,建议使用 flutter_webview_plugin,否则,建议使用 native WebView。


再读一篇类似文章?推荐阅读姊妹篇:

Flutter 与 iOS 的 WebView 对比


如有任何知识产权、版权问题或理论错误,还请指正。
https://xiangwushuo.github.io/posts/Flutter-Android-Webview
本文作者 Zackratos,转载请注明原作者及以上信息。

评论和共享

上海 T 沙龙移动实践日于近期成功举办,我司工程师 Rolland Safort(中文名:塞福)有幸成为了受邀嘉宾前往进行技术分享。塞福目前负责享物说小程序开发工作,有多年前端工作经验,对前端开发有深入的理解。他为大家带来了享物说团队小程序开发过程中积累的一些架构与性能优化方案。

一、小程序简介

因为在场的同学有很大一部分是 iOS 和 Android 开发,对 Web 端和小程序开发并不是十分了解,所以塞福首先对小程序的基本组成和实现原理进行了简单的介绍。

微信小程序运行在三端:iOS、Android 和用于调试的开发者工具中。本质上都是通过各个平台的 WebView 和 JS 解析器来进行渲染和解释运行。

JavaScript:微信小程序的 JavaScript 运行在微信 App 的上下文中,不能直接操作 DOM,也不能通过 Node.js 相关接口访问操作系统 API。在 iOS 上,小程序的 JavaScript 代码是运行在 JavaScriptCore 中;在 Android 上,小程序的 JavaScript 代码是通过 X5 内核来解析的;在开发工具上,小程序的 JavaScript 代码是运行在 NW.js (基于 Chromium 和 Node.js 运行,以前也叫 nodeWebkit)上的。

WXML:微信小程序的展示层,是微信自己定义的基于 XML 语法的描述语言。

WXSS:用来修饰 WXML 展示层的样式,类似 CSS 和 HTML 的关系。WXSS(WeiXin Style Sheets)是一套样式语言,用于描述 WXML 的组件样式。WXSS 用来决定 WXML 的组件应该怎么显示。WXSS 具有 CSS 大部分特性,对 CSS 进行了扩充以及修改。

小程序的开发当中也有一些注意点需要关注:

  1. 小程序发起的都是 HTTPS 网络请求,在开发调试的过程中可以不校验协议和 TLS 版本,但在实际上线后必须进行 HTTPS 通信;
  2. 微信小程序名称在帐号信息设置时完成,名称可以由中文、数字、英文,长度在 3-20 个字符之间,一个中文字等于 2 个字符,一旦设置完成后需要修改的话,300RMB 可修改一次(别问我是怎么知道的),且有次数限制;
  3. 小程序开发完成后打包有大小限制,最大每个包 2M,最多 4 个包,一些占包体积较大的资源需要尽量压缩。

大家可以大概想象一下小程序的执行过程:

  1. 用户在微信中点击、打开某一个小程序;
  2. 微信 App 从服务器下载这个小程序包;
  3. 微信内置的运行环境分析小程序包得到应用程序的配置信息加载并运行 app.js;
  4. 根据用户操作进一步响应各种用户行为。

二、享物说小程序

享物说小程序是一个比较大型的小程序,因为享物说整个平台都是小程序起家的,所以小程序承载了享物说平台的几乎全部功能,iOS 和 Android 的原生 App 虽然已经发布了一些版本,但还在完善和丰富功能的过程中。

1. 发展历史、架构

享物说小程序目前主要经历过一次大规模的重构,之前的旧版本使用了微信官方的小程序开发框架,而新版采用美团的 mpvue 进行重构,在性能和体验上有了进一步的提升。

使用 mpvue 主要有以下一些优势:

  1. 彻底的组件化开发能力,提高代码复用性;
  2. Vuex 数据管理方案,方便构建复杂应用;
  3. 快捷的 webpack 构建机制,自定义构建策略、开发阶段 hotReload,提升开发效率;
  4. 支持使用 npm 外部依赖;
  5. 支持 H5 代码转换编译成小程序代码。

享物说小程序从 2017 年9 月上线,至今已有超过 2000 万用户使用,上图展示了享物说用户量变化的一个大致趋势。

2. 埋点、测试、监控

小程序相比原生 App,最显著的一个特征就是可以快速迭代,虽然它的主包发版也需要一定的审核时间,但是相比原生 App,小程序可以通过将大部分资源和配置部署到服务器上来实现变相的热修改 / 热更新,这为产品的灰度发布、BUG 修正、功能开发带来了极大的便利。

正因为小程序的灵活性给业务带来的便利性,需要去对它的埋点和检测进行一个比较好的维护,不仅限于小程序的页面访问、用户操作、事件触发等,还要尽可能追踪每一个分享出去的 URL 的传播轨迹和访问流量,根据所得数据来进行分析从而为接下来的业务方向指路。

三、性能与体验优化

享物说小程序上线至今未满一年,成长速度可以用夸张来形容,但令团队欣喜之余,带来的还有严峻的业务压力和流量高峰挑战。

以下这些是团队的部分小程序优化经验和建议:

  1. 少⽤ setData,如果不需要渲染可以使⽤ this.data.key = value;
  2. 尽量使用 CDN,图片都用懒加载;
  3. 使⽤用分包加载;
  4. 优化代码,清理没有使⽤到的代码和资源 ,减小包体积;
  5. 开发环境添加 ESlint 代码校验;
  6. 分享图片及其他经常变的静态资源,不能写死在前端,通过接口动态获取,减少重新发版的可能性。

四、高可⽤的⼩程序

面对一个用户量如此大的平台,传统的解决方案可能都会有些捉襟见肘,享物说小程序都做了哪些来获得相对稳定的线上运行状态呢?

1. 超时设置

  1. 网络请求,默认超时时间和最大超时时间都 60s;
  2. requestuploadFiledownloadFile 的最大并发限制是 10 个 超出最大并发数的请求,会等待前面的请求完成或者超时。

2. 服务降级

根据后端负载和可⽤状况,结合之前所说的服务分级,把后端的服务等级做响应的调整。如果服务端负载过重,那么可以有针对地拒绝服务或者关闭服务。高峰期间,对⾮核心业务,超出服务端负荷的情况下,可以考虑暂时关闭服务。

在极端情况下,可以考虑仅在 CDN 提供只读的静态内容。

3. 服务监控

微信官⽅给⼩程序提供了运维中⼼,在这⾥可以看到小程序的近乎实时的运⾏⽇志。如有必要,可以埋点收集在异常发⽣时候的⽤户操作路径,帮助事后复现问题。

4. Webviews

活动和部分⾮核心业务使⽤ H5 实现,并且通过 WebView 在⼩程序内展示。

五、总结

享物说小程序上线近一年过程中最大的问题就是流量压力导致的性能和稳定性问题,团队的同学通过如下一些手段对该问题进行了解决:

  1. 使用 mpvue 对整个小程序进行重构;
  2. 添加完善的埋点并结合微信提供的运维中心对整个线上运行状况进行完整的监控和管理;
  3. 核心业务整理 / 优化,非核心业务 H5 化,必要时非核心业务降级,保证主业务可用性;
  4. 资源与配置动态话,实现无需发版的线上热修改 / 热更新。

六、其他

对本次分享内容感兴趣的同学可下载相关资料查看详情:

  1. 视频:https://v.youku.com
  2. PPT:https://github.com/Code-T/salon-resources

本文编写过程中参考了以下文章,在此对原作者们表示感谢:

  1. mpvue 官方文档
  2. NW.js 入坑指南
  3. 微信小程序原理
  4. 微信小程序官方文档

如有任何知识产权、版权问题或理论错误,还请指正。
https://xiangwushuo.github.io/posts/CodeT-Share
本文作者 EyreFree,转载请注明原作者及以上信息。

评论和共享

  • 第 1 页 共 1 页
作者的图片

享物说

官方技术博客


研发


上海