APP 常用跨端技术栈深入分析
导读
本文主要针对常用跨端技术 Flutter、ReactNative、Weex、H5,从技术特点、基本架构、编译原理、基本渲染流程等进行梳理分析;以及一些常见性能问题如何优化解决,然后如何进行技术选型或在进行业务开发时选择不同技术栈的逻辑是什么。
01
背景
在今年的敏捷团队建设中,我通过 Suite 执行器实现了一键自动化单元测试。Juint 除了 Suite 执行器还有哪些执行器呢?由此我的 Runner 探索之旅开始了!
随着技术的发展,产生了越来越多的端,如 Android、iOS、Mac、Windows、Web、Fuchsia OS、鸿蒙等,而随着公司业务的发展,出现了越来越多的业务场景;作为 APP 开发人员,在日常工作中难免会碰到以下问题,如:1、UI 设计师在进行 UI 审查时、测试同学在回归测试过程中、业务方在使用过程中,多少会发现端与端存在着差异,影响用户体验;2、同样的业务、同样的功能在不同的端上,需要每端投入资源去开发实现。而移动互联网的发展已经处于晚期,领导们越来越关心投入产出。
与此同时,出现了一些跨端的技术解决方案,可以实现一套代码在多端运行,解决业务发展上的痛点,如 Flutter、ReactNative、Weex、H5(注:小程序和其它基于 DSL 的方案暂不在本文讨论范围)。然后对一些常用 APP 进行了对比分析,结论和预期一致,大部分都在使用跨端技术;Flutter 和 ReactNative 使用率较高,Weex 使用率相对低一些,H5 基本都在使用,使用多种跨端技术框架是一种常态。那么,它们都有哪些特点呢?
02
四种技术栈特点介绍
理解,首先 MCube 会依据模板缓存状态判断是否需要网络获取最新模板,当获取到模板后进行模板加载,加载阶段会将产物转换为视图树的结构,转换完成后将通过表达式引擎解析表达式并取得正确的值,通过事件解析引擎解析用户自定义事件并完成事件的绑定,完成解析赋值以及事件绑定后进行视图的渲染,最终将目标页面展示到屏幕。
图 1-技术栈特点
通过图 1,从性能、开发语言、渲染、包大小、社区、支持平台等方面梳理了它们的主要特点;不由产生几个问题:为什么原生和 Flutter 性能更好?为什么 ReactNative 和 Weex 性能相对较差?为什么 H5 页加载慢?这些性能问题该如何去优化,这是需要深入了解的问题,下面将从基本的架构、渲染流程、编译运行原理等一起分析。
03 基础架构介绍
3.1 Flutter 基础架构介绍
ABM 是 Apple 公司提供的 iOS 应用的分发渠道之一,与 App Store 平台不同,ABM 是 2019 年 10 月才开始在中国区启动的一套全新的应用分发系统,部分功能和企业账号类似,旨在为企业提供快速、高效的方式来部署应用到企业拥有的苹果设备。ABM 与 App Store 两个平台的关键区别如下:
图 2-Flutter 基础架构
Google 在 2018 年发布了 Flutter 1.0,如图 2 所示,主要分为 Framework 层和 Engine 层;
Framework 层:基于 Dart 实现,主要包括 Text、Image、Button、动画、手势等各种 Widgets,核心基础类库 io、async、ui 等 package;基于 Framework 开发 App,其运行在 Engine 层上,Framework 和逻辑层都在基于 Dart 语言开发,对于开发而言,一切都是 Widget,Widget 是 UI 实现的基础;Engine 层:基于 C++、C 实现;主要包括 Skia 渲染引擎库、Dart Runtime、Text 文本渲染库等,而 Engine 层自带 Skia 渲染引擎,以此实现所有端的渲染展示统一,在 Engine 层适配平台差异和跨平台支持,实现更完美的跨端效果;Dart 代码通过 AOT 编译为运行平台的二进制代码。也就是说 Flutter 不需要桥接,自己完成从逻辑侧和渲染侧的所有能力,和原生类似。这也是它性能突出的关键所在。另外 Android 自带 Skia 引擎,所以也使得在 Android 的的编译产物比 iOS 更小。除了支持移动端外,还支持 Mac OS、Windows 等 PC 端和 Web 端,在新的 Funchsia OS 也支持 Dart,使用 Flutter 作为 UI 框架。
对于 Flutter Web,Framework 层是公用的,意味着业务层可以使用此层的 widgets 实现逻辑跨端;但 Engine 层则不同,需要通过 Canvas Render 或者 HTML Render 对齐 Engine 的能力。2022 年 5 月 Google IO 大会发布 Flutter 3.0,除了移动端,更好的支持了 Mac OS、Linux 平台,也包括其它一系列优化和支持,大家可以多关注。
3.2 ReactNative 基础架构介绍
ABM 是 Apple 公司提供的 iOS 应用的分发渠道之一,与 App Store 平台不同,ABM 是 2019 年 10 月才开始在中国区启动的一套全新的应用分发系统,部分功能和企业账号类似,旨在为企业提供快速、高效的方式来部署应用到企业拥有的苹果设备。ABM 与 App Store 两个平台的关键区别如下:
图 3-ReactNative 基础架构
ReactNative 是 Facebook 于 2015 年开源,如图 3 所示,主要服务于 Android 和 iOS 两端,采用 React 开发实现逻辑侧代码(也可应用于前端),采用 Redux 实现状态管理,在 APP 中 UI 渲染、网络请求、动画等均由原生侧桥接实现;在这里实际运行过程中,js 侧的 dom 会形成一个 virtual dom,并通过 bridge 桥接将此 dom 结构传输到原生侧,原生侧会解析并映射到原生控件,形成原生的 dom 结构后,再调用原生能力进行渲染展示。
2021 年 ReactNative 新版本对底层进行了重构,可以关注一下,如改变线程模型,引入异步渲染能力,允许多个渲染并简化异步数据处理,简化 JSBridge 等。
3.3 Weex 基础架构介绍
图 4-Weex 基础架构
Weex 是阿里 2016 年发布的跨端框架,如图 4 所示,Weex 编译产物 js bundle 可以部署在服务端,APP 加载完即可运行,也可以看出具备动态发布的能力;和 ReactNative 类似,Weex 在实际运行过程中,js 侧会形成一个 dom,并通过 Bridge 交由原生侧解析,映射到原生控件再由原生能力进行渲染;Weex 基于 JS V8 引擎,基于 Vue 设计,支持 Android、iOS、Web 三端。
3.4 WebView 基础架构介绍
图 5-WebView 内核基础架构
WebView 内核模块较复杂,如图 5 所示,这里主要介绍 WebView 架构主要的几个部分:桥接协议是上层逻辑测与 WebView 的通信层,是 JS 和 Native 互相通信的能力层;
WebCore 是浏览器加载和排版渲染页面的基础,主要包括资源加载、HTML 解析、CSS 解析、DOM 解析、排版渲染等,JavaScript 引擎是 JavaScript 解析器,JavaScriptCore 是 Webkit 的 JavaScript 引擎,V8 是 Google 的 Blink 的默认引擎;WebKit Ports 是 WebKit 中移植部分,包括网络、字体、图片解码、音视频解码、硬件加速等模块;然后再往下也使用了很多第三方库,包括 2D 图形库、3D 图形库、网络库、存储库、音视频库等;最底层是操作系统,支持 Android、iOS、Windows 等系统。
3.5 编译原理分析
Flutter 支持 Release、Profile、Debug 编译模式。
Release 模式即使用 AOT 预编译模式,预编译为机器码,通过编译生成对应架构的代码,在用户设备上直接运行对应的机器码,运行速度快,执行性能好;此模式关闭了所有调试工具,只支持真机。对于编译产物,iOS 侧主要生成 App.framework 和 Flutter.framework;App.framework 为 dart 代码编译产物,Flutter.framework 为引擎编译产物;Android 侧主要在 lib 下增加了 libapp.so 和 libflutter.so,libapp.so 为 dart 代码编译产物,libflutter.so 为引擎编译产物,不同的是在 assets 下增加了 flutter_assets 存放引用资源文件。
Profile 模式和 Release 模式类似,此模式最重要的作用是可以用 DevTools 来检测应用的性能,做性能调试分析。
Debug 模式使用 JIT 即时编译技术,支持常用的开发调试功能 hot reload,在开发调试时使用,包括支持的调试信息、服务扩展、Observatory、DevTools 等调试工具,支持模拟器和真机。iOS 侧主要生成 App.framework 和 Flutter.framework,在 App.framework 文件夹里多了 isolate_snapshot_data,kernel_blob.bin,vm_snapshot_data;Android 侧也同样多了多了以上文件,但 lib 下少了 libapp.so 文件。
ReactNative 整体分为逻辑侧和渲染侧,逻辑侧基于 js 引擎,会将基于 React 写的代码编译为 JavaScript 原生代码,再编译生成 jsbundle 文件,内置或下发到 APP 端运行;而渲染侧依赖于 Android 或 iOS 原生渲染,需要分平台编译对应的编译产物,然后发布到服务端或内置到 APP。
Weex 和 ReactNative 类似,weex 会将源码编译为 js bundle,这些 js bundle 可以部署在服务端,APP 下载完 js bundle 后,通过 js 引擎构建虚拟 dom 并通过桥接映射到原生 dom,由原生渲染引擎进行渲染。
H5:以 React 和 Vue 为例,会将以框架开发的代码编译为 JavaScript 原生代码,即然后在浏览器或者 WebView 中执行;内核会先建立连接、加载资源,然后解析、排版布局、绘制渲染呈现给用户。
3.6 基本渲染流程对比
图 6-基本渲染流程对比
简单分析渲染流程,基于 Android 和 iOS 原生开发 APP,调用 Framework 框架层实现上层逻辑,经过布局绘制后直接调用系统渲染引擎进行渲染展示;基于 Flutter 开发 APP,会直接调用 Skia 渲染引擎进行渲染展示;不依赖于原生渲染。
基于 ReactNative 或 Weex 开发 APP 则不同,首先业务逻辑是基于 React 或 Weex 开发,然后会将 js bundle 包预置或下载到 APP,然后将虚拟 dom 通过 bridge 映射到原生控件,再调用原生渲染引擎进行渲染展示。
基于 Hybrid 方案开发 APP,需要通过 React、Vue 等前端框架实现,首页要编译为 JavaScript 原生语言,然后通过链接在 WebView 或浏览器加载页面,关键的流程是连接加载、解析、排版、绘制,最后再调渲染引擎进行展示。
通过以上所有分析,可以回答前面提出的问题:
为什么原生和 Flutter 性能更好?主是都是经过布局绘制后直接调系统或自带渲染引擎进行展示。
为什么 ReactNative 和 Weex 性能相对慢?主要是需要下载 js bundle 包,并把 js dom 结构解析映射到原生,而下载和预置都比较耗时,并且依赖原生进行渲染(ReactNative 新版本升级了基础架构,据说有较大性能提升,大家也可以关注)。
为什么 H5 页加载慢?主要因为连接和加载比较耗时,这里占大部分时间,连接和加载完以后基本就是 WebView 或浏览器本地可以完成的工作,后期优化也可以以此为切入点。
04 常见主要性能问题优化
在实际开发过程中也遇到了一些性能问题,接下来进行简单介绍。
4.1 如何优化 Flutter 性能?
关键优化指标:页面异常率、页面 FPS 帧率、页面加载时长。
页面异常率(异常发生次数 / 整体页面 PV 数):通过 runZoned 与 FlutterError 两个方法,在异常拦截的方法中统计异常的发生次数和堆栈数据。
页面 FPS 帧率:如何采集 FPS 是关键,通过 window 对象注册 onReportTimings 回调,就可以得到整个构建和渲染过程的耗时,然后就可以算出页面的 FPS。
页面加载时长(页面可见的时间-页面创建的时间):页面可见的时间通过 WidgetsBinding 的 addPostFrameCallback 回调获取,页面创建的时间通过页面初始化方法 initState 获取。
将以上数据上传到监控和性能分析平台(mPaaS 和烛龙),作为后期性能分析和优化的参考数据,在开发过程中可通过 DevToos 性能分析工具、Flutter Inspector 分析优化性能。按需加载,局部刷新也是常用的优化手段。其它性能优化如布局加载优化、状态管理优化、启动优化-引擎预加载、内存优化、包大小优化等不再详细介绍。可以多关注 Flutter 社区,定期升级 Flutter 版本,会带来很好的收获。
4.2 如何优化 ReactNative 加载慢的问题?
一是可以预下载 bundle 包,减少包加载的时间,打开页面直接映射渲染,从而达到更快打开页面的目的,当然也可以预置包,需要平衡好包大小和性能;
二是尝试升级 ReactNative 最新版本,新版本升级了基础架构,主要包括线程模型,引入了异步渲染能力,优化 JSBridge,对性能有明显提升(作者咨询过京东 mPaaS 团队,也在跟进中)。
4.3 如何优化 APP 中 H5 加载慢的问题
图 7-加载 H5 流程介绍
图 7 描述了从 WebView 初始化到 H5 页面最终渲染的整个过程,以及和前面 H5 基本渲染流程进行分析。耗时环节的主要有两点,一是 WebView 初始化,可以通过提前初始化 WebView 优化此问题;二是资源(html、js、css\图片等)的请求连接和加载,可以用 H5 离线包方案解决此问题,通过资源的预加载,解决 html、js、css 和资源图片的加载问题,从而大大降低资源的加载时间,提升页面加载性能,甚至达到秒开的效果。
05 总结
那么如何技术选型呢?应该以提升开发效率和用户体验为前提去思考,然后再分析关键因素:
1、技术栈的基础架构如何,原始架构是否优秀,是否更面向未来发展;
2、团队技术栈成熟度,学习的成本,社区的成熟度;
3、研发效率,实现代码多端复用,减少多端差异,降低开发成本,更加专注于业务开发;而效率提升是一个持续的收益过程,体现在业务发展的整个生命周期。当然,对于新技术在实践前期会有一些成本,但熟悉后总的收益是长期的;
4、是否更好解决多端一致性,更好解决 UI 设计师在 UI 审查时、测试同学在测试过程中、业务方在使用过程中发现的端与端并异问题,风格统一也是良好用户体验的重要体现;
5、支持动态化的程度,解决新需求必须发版的问题,也是业务的痛点,关键因素;
6、用户体验是最关键的,也需考虑用户的使用环境(网络环境、手机配置)等;
对于正式的 C 端项目,面对千万甚至亿级的用户量,技术选型策略一定是在保证用户体验的基础上实现降本提效,用户第一,用户利益最大化即保证了公司的利益;对于非 C 端项目,可能需要考虑在实现降本提效基础上提升用户体验。
本文作者:京东国际技术研发部——卢旭、张公、姚峰、高鑫鹏、李澄锋、陈海蛟、高明、凡为连、单禹钦、慕新建
评论