1.1 跨平台技术简介
纯原生开发主要面临动态化和开发成本两个问题,而针对这两个问题,诞生了一些跨平台的动态化框架
针对原生开发面临问题,业界一直都在努力寻找好的解决方案,而时至今日,已经有很多跨平台框架(注意,本书中所指的“跨平台”若无特殊说明,即特指 Android 和 iOS 两个平台),根据其原理,主要分为三类:
- H5 + 原生(Cordova、Ionic、微信小程序)
- JavaScript 开发 + 原生渲染 (React Native、Weex)
- 自绘UI + 原生 (Qt for mobile、Flutter)
1.2 Hybrid技术简介
这类框架主要原理就是将 App 中需要动态变动的内容通过HTML5(简称 H5)来实现,通过原生的网页加载控件WebView (Android)或 WKWebView(iOS)来加载
目前混合开发框架的典型代表有:Cordova、Ionic 。大多数 App 中都会有一些功能是 H5 开发的,至少目前为止,HTMLybrid App 仍然是最通用且最成熟的跨端解决方案
混合应用的优点是动态内容是 H5,Web 技术栈,社区及资源丰富,缺点是性能体验不佳,对于复杂用户界面或动画,WebView 有时会不堪重任。
1.3 React Native、Weex
主要介绍一下 JavaScript开发 + 原生渲染 的跨平台框架原理。
React Native (简称 RN )是 Facebook 于 2015 年 4 月开源的跨平台移动应用开发框架,是 Facebook 早先开源的 Web 框架 React 在原生移动应用平台的衍生产物,目前支持 iOS 和 Android 两个平台。RN 使用JSX 语言(扩展后的 JavaScript,主要是可以在 JavaScript 中写 HTML标签)和 CSS 来开发移动应用。因此,熟悉 Web 前端开发的技术人员只需很少的学习就可以进入移动应用开发领域。
由于 RN 和 React 原理相通,并且 Flutter在应用层也是受 React 启发,很多思想也都是相通的,因此,我们有必要深入了解一下React原理。
React 是一个响应式的 Web 框架,我们先了解一下两个重要的概念:DOM 树与响应式编程
DOM树与控件树
文档对象模型(Document Object Model,简称DOM),这是表示和处理一个 HTML 或XML 文档的标准接口。简单来说,DOM 就是文档树,与用户界面控件树对应,在前端开发中通常指 HTML 对应的渲染树,但广义的 DOM 也可以指 Android 中的 XML 布局文件对应的控件树,而术语DOM操作就是指直接来操作渲染树(或控件树), 因此,可以看到其实 DOM 树和控件树是等价的概念,只不过前者常用于 Web 开发中,而后者常用于原生开发中
响应式编程
React 中提出一个重要思想:状态改变则UI随之自动改变。而 React 框架本身就是响应用户状态改变的事件而执行重新构建用户界面的工作,这就是典型的 响应式 编程范式,下面我们总结一下 React 中响应式原理:
- 开发者只需关注状态转移(数据),当状态发生变化,React 框架会自动根据新的状态重新构建UI。
- React 框架在接收到用户状态改变通知后,会根据当前渲染树,结合最新的状态改变,通过 Diff 算法,计算出树中变化的部分,然后只更新变化的部分(DOM操作),从而避免整棵树重构,提高性能。
值得注意的是,在第二步中,状态变化后 React 框架并不会立即去计算并渲染 DOM 树的变化部分,相反,React会在 DOM 树的基础上建立一个抽象层,即虚拟DOM树,对数据和状态所做的任何改动,都会被自动且高效的同步到虚拟 DOM ,最后再批量同步到真实 DOM 中,而不是每次改变都去操作一下DOM。
为什么不能每次改变都直接去操作 DOM 树?这是因为在浏览器中每一次 DOM 操作都有可能引起浏览器的重绘或回流(重新排版布局,确定 DOM 节点的大小和位置):
- 如果 DOM 只是外观风格发生变化,如颜色变化,会导致浏览器重绘界面。
- 如果 DOM 树的结构发生变化,如尺寸、布局、节点隐藏等导致,浏览器就需要回流。
React Native
React Native 是 React 在原生移动应用平台的衍生产物,那两者主要的区别是什么呢?其实,主要的区别在于虚拟 DOM 映射的对象是什么。React中虚拟 DOM 最终会映射为浏览器 DOM 树,而 RN 中虚拟 DOM会通过 JavaScriptCore 映射为原生控件
JavaScriptCore 是一个JavaScript解释器,它在React Native中主要有两个作用:
- 为 JavaScript 提供运行环境。
- 是 JavaScript 与原生应用之间通信的桥梁,作用和 JsBridge 一样,事实上,在 iOS 中,很多 JsBridge 的实现都是基于 JavaScriptCore 。
而 RN 中将虚拟 DOM 映射为原生控件的过程主要分两步:
- 布局消息传递; 将虚拟 DOM 布局信息传递给原生;
- 原生根据布局信息通过对应的原生控件渲染;
至此,React Native 便实现了跨平台,相对于混合应用,由于React Native是 原生控件渲染,所以性能会比混合应用中 H5 好一些
Weex
Weex 是阿里巴巴于 2016 年发布的跨平台移动端开发框架,思想及原理和 React Native 类似,底层都是通过原生渲染的,不同是应用层开发语法 (即 DSL,Domain Specific Language):Weex 支持 Vue 语法和 Rax 语法,Rax 的 DSL(Domain Specific Language) 语法是基于 React JSX 语法而创造,而 RN 的 DSL 是基于 React 的,不支持 Vue
总结
JavaScript 开发 + 原生渲染 的方式主要优点如下:
- 采用 Web 开发技术栈,社区庞大、上手快、开发成本相对较低。
- 原生渲染,性能相比 H5 提高很多。
- 动态化较好,支持热更新。
不足:
- 渲染时需要 JavaScript 和原生之间通信,在有些场景如拖动可能会因为通信频繁导致卡顿。
- JavaScript 为脚本语言,执行时需要解释执行 (这种执行方式通常称为 JIT,即 Just In Time,指在执行时实时生成机器码),执行效率和编译类语言(编译类语言的执行方式为 AOT ,即 Ahead Of Time,指在代码执行前已经将源码进行了预处理,这种预处理通常情况下是将源码编译为机器码或某种中间码)仍有差距。
- 由于渲染依赖原生控件,不同平台的控件需要单独维护,并且当系统更新时,社区控件可能会滞后;除此之外,其控件系统也会受到原生UI系统限制,例如,在 Android 中,手势冲突消歧规则是固定的,这在使用不同人写的控件嵌套时,手势冲突问题将会变得非常棘手。这就会导致,如果需要自定义原生渲染组件时,开发和维护成本过高
1.4 Qt Mobile
我们看看最后一种跨平台技术:自绘UI + 原生。这种技术的思路是:通过在不同平台实现一个统一接口的渲染引擎来绘制UI,而不依赖系统原生控件,所以可以做到不同平台UI的一致性。
注意,自绘引擎解决的是 UI 的跨平台问题,如果涉及其它系统能力调用,依然要涉及原生开发。这种平台技术的优点如下:
- 性能高;由于自绘引擎是直接调用系统API来绘制UI,所以性能和原生控件接近。
- 灵活、组件库易维护、UI外观保真度和一致性高;由于UI渲染不依赖原生控件,也就不需要根据不同平台的控件单独维护一套组件库,所以代码容易维护。由于组件库是同一套代码、同一个渲染引擎,所以在不同平台,组件显示外观可以做到高保真和高一致性;另外,由于不依赖原生控件,也就不会受原生布局系统的限制,这样布局系统会非常灵活。
不足:
- 动态性不足;为了保证UI绘制性能,自绘UI系统一般都会采用 AOT 模式编译其发布包,所以应用发布后,不能像 Hybrid 和 RN 那些使用 JavaScript(JIT)作为开发语言的框架那样动态下发代码。
- 应用开发效率低:Qt 使用 C++ 作为其开发语言,而编程效率是直接会影响 App 开发效率的,C++ 作为一门静态语言,在 UI 开发方面灵活性不及 JavaScript 这样的动态语言,另外,C++需要开发者手动去管理内存分配,没有 JavaScript 及Java中垃圾回收(GC)的机制。
也许你已经猜到 Flutter 就属于这一类跨平台技术,没错,Flutter 正是实现一套自绘引擎,并拥有一套自己的 UI 布局系统,且同时在开发效率上有了很大突破。不过,自绘制引擎的思路并不是什么新概念,Flutter并不是第一个尝试这么做的,在它之前有一个典型的代表,即大名鼎鼎的Qt。
1.5 Flutter出世
“千呼万唤始出来”,铺垫这么久,现在终于等到主角出场了!
Flutter 是 Google 发布的一个用于创建跨平台、高性能移动应用的框架。Flutter 和 Qt mobile 一样,都没有使用原生控件,相反都实现了一个自绘引擎,使用自身的布局、绘制系统。那么,我们会担心,Qt mobile 面对的问题Flutter是否也一样,Flutter会不会步入Qt mobile后尘,成为另一个烈士?要回到这个问题,我们先来看看Flutter诞生过程:从 2017 年 Google I/O 大会上,Google 首次发布 Flutter 到 2021年8月底,已经有 127K 的 Star,Star 数量 Github 上排名前 20 。经历了4年多的时间,Flutter 生态系统得以快速增长,国内外有非常基于 Flutter 的成功案例,国内的互联网公司基本都有专门的 Flutter 团队。总之,历时 4 年,Flutter 发展飞快,已在业界得到了广泛的关注和认可,在开发者中受到了热烈的欢迎,成为了移动跨端开发中最受欢迎的框架之一。
现在,我们来和 Qt mobile做一个对比:
- 生态:Flutter 生态系统发展迅速,社区非常活跃,无论是开发者数量还是第三方组件都已经非常可观。
- 技术支持:现在 Google 正在大力推广Flutter,Flutter 的作者中很多人都是来自Chromium团队,并且 Github上活跃度很高。另一个角度,从 Flutter 诞生到现在,频繁的版本发布也可以看出 Google 对 Flutter的投入的资源不小,所以在官方技术支持这方面,大可不必担心。
- 开发效率:一套代码,多端运行;并且在开发过程中 Flutter 的热重载可帮助开发者快速地进行测试、构建UI、添加功能并更快地修复错误。在 iOS 和 Android 模拟器或真机上可以实现毫秒级热重载,并且不会丢失状态。这真的很棒,相信我,如果你是一名原生开发者,体验了Flutter开发流后,很可能就不想重新回去做原生了,毕竟很少有人不吐槽原生开发的编译速度。
1.6 总结
主要介绍了目前移动开发中三种跨平台技术,现在我们从框架角度对比一下它们,如表所示:
技术类型 | UI渲染方式 | 性能 | 开发效率 | 动态化 | 框架代表 |
---|---|---|---|---|---|
H5 + 原生 | WebView渲染 | 一般 | 高 | 支持 | Cordova、Ionic |
JavaScript + 原生渲染 | 原生控件渲染 | 好 | 中 | 支持 | RN、Weex |
自绘UI + 原生 | 调用系统API渲染 | 好 | Flutter高, Qt低 | 默认不支持 | Qt、Flutter |
上表中开发语言主要指应用层的开发语言,而开发效率,是指整个开发周期的效率,包括编码时间、调试时间、以及排错、处理兼容性问题时间。动态化主要指是否支持动态下发代码和是否支持热更新。值得注意的是 Flutter 的Release 包默认是使用 Dart AOT 模式编译的,所以不支持动态化,但 Dart 还有 JIT 或 snapshot 运行方式,这些模式都是支持动态化的。
网友评论