You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

136 lines
14 KiB
Markdown

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# 03 | 深入理解跨平台方案的历史发展逻辑
你好,我是陈航。
今天,我会从跨平台开发方案的诞生背景、原理和发展历史的角度,和你聊聊这些常见的开发方案,以及针对不同的场景我们应该如何选择对应的方案。
## 浅述跨平台开发的背景
我们当下所处的移动互联网时代,以它独有的变革方式,带给我们快捷、经济、安全和方便,改变着生活的方方面面。而对于企业来说,移动应用已然成为各类手机终端上一张必备的产品名片。
在移动互联网的浪潮下我们开发的应用要想取胜开发效率和使用体验可以说是同等重要。但是使用原生的方式来开发App就要求我们必须针对iOS和Android这两个平台分别开发这对于中小型团队来说就是隐患和额外的负担。
因为这样的话我们不仅需要在不同的项目间尝试用不同的语言去实现同样的功能还要承担由此带来的维护任务。如果还要继续向其他平台比如Web、Mac或Windows拓展的话我们需要付出的成本和时间将成倍增长。而这显然是难以接受的。于是跨平台开发的概念顺势走进了我们的视野。
所以从本质上讲,**跨平台开发是为了增加业务代码的复用率,减少因为要适配多个平台带来的工作量,从而降低开发成本。**在提高业务专注度的同时,能够为用户提供一致的用户体验。用一个词来概括这些好处的话,就是“多快好省”。
“一次编码到处运行”。二十多年前Java正是以跨平台特性的口号登场击败了众多竞争对手。这个口号意味着Java可以在任何平台上进行开发然后编译成一段标准的字节码后就可以运行在任何安装有Java虚拟机JVM的设备上。虽然现在跨平台已经不是Java的最大优势而是它繁荣的生态但不可否认它当年打着跨平台旗号横空出世确实势不可挡。
而对于移动端开发来讲,如果能实现“一套代码,多端运行”,这样的技术势必会引发新的生产力变革,在目前多终端时代的大环境下,可以为企业节省人力资源上,从而带来直接的经济效益。
伴随着移动端的诞生和繁荣为了满足人们对开发效率和用户体验的不懈追求各种跨平台的开发方案也如雨后春笋般涌现。除了React Native和Flutter之外这几年还出现过许多其他的解决方案接下来我将会为你一一分析这些方案。这样你在选择适合自己的移动开发框架时也就有章可循了。
在此我特地强调一下我在下文提到的跨平台开发方案如果没有特殊说明的话指的就是跨iOS和Android开发。
## 跨平台开发方案的三个时代
根据实现方式的不同,业内常见的观点是将主流的跨平台方案划分为三个时代。
* Web容器时代基于Web相关技术通过浏览器组件来实现界面及功能典型的框架包括Cordova(PhoneGap)、Ionic和微信小程序。
* 泛Web容器时代采用类Web标准进行开发但在运行时把绘制和渲染交由原生系统接管的技术代表框架有React Native、Weex和快应用广义的还包括天猫的Virtual View等。
* 自绘引擎时代自带渲染引擎客户端仅提供一块画布即可获得从业务逻辑到功能呈现的多端高度一致的渲染体验。Flutter是为数不多的代表。
接下来我们先看一下目前使用最广泛的Web容器方案。
### Web容器时代
Web时代的方案主要采用的是原生应用内嵌浏览器控件WebViewiOS为UIWebView或WKWebViewAndroid为WebView的方式进行HTML5页面渲染并定义HTML5与原生代码交互协议将部分原生系统能力暴露给HTML5从而扩展HTML5的边界。这类交互协议就是我们通常说的JS Bridge
这种开发模式既有原生应用代码又有Web应用代码因此又被称为Hybrid开发模式。由于HTML5代码只需要开发一次就能同时在多个系统运行因此大大降低了开发成本。
由于采用了Web开发技术社区和资源非常丰富开发效率也很高。但**一个完整HTML5页面的展示要经历浏览器控件的加载、解析和渲染三大过程性能消耗要比原生开发增加N个数量级**。
接下来,我以加载过程为例,和你说明这个过程的复杂性。
1. 浏览器控件加载HTML5页面的HTML主文档
2. 加载过程中遇到外部CSS文件浏览器另外发出一个请求来获取CSS文件
3. 遇到图片资源浏览器也会另外发出一个请求来获取图片资源。这是异步请求并不会影响HTML文档的加载。
4. 加载过程中遇到JavaScript文件由于JavaScript代码可能会修改DOM树因此HTML文档会挂起渲染加载解析渲染同步的线程直到JavaScript文件加载解析并执行完毕才可以恢复HTML文档的渲染线程。
5. JavaScript代码中有用到CSS文件中的属性样式于是阻塞等待CSS加载完毕才能恢复执行。
而这只是完成HTML5页面渲染的最基础的加载过程。加载、解析和渲染这三个过程在实际运行时又不是完全独立的还会有交叉。也就是说会存在一边加载一边解析一边渲染的现象。这就使得页面的展示并不像想象中那么容易。
通过上面的分析你可以看出一个HTML5页面的展示是多么得复杂这和原生开发通过简单直接的创建控件设置属性后即可完成页面渲染有非常大的差异。Web与原生在UI渲染与系统功能调用上各司其职因此这个时代的框架在Web与原生系统间还有比较明显的、甚至肉眼可见的边界。
![](https://static001.geekbang.org/resource/image/6d/79/6d4035e44b4af68b7588a750fec06c79.png)
图1 Hybrid开发框架
我也曾碰到过很多人觉得跨平台开发不靠谱。但其实Web容器方案是跨平台开发历史上最成功的例子。也正是因为它太成功了以至于很多人都忽略了它也是跨平台方案之一。
### 泛Web容器时代
虽然Web容器方案具有生态繁荣、开发体验友好、生产效率高、跨平台兼容性强等优势但它最大的问题在于承载着大量Web标准的Web容器过于笨重以至于性能和体验都达不到与原生同样的水准在复杂交互和动画上较难实现出优良的用户体验。
而在实际的产品功能研发中我们通常只会用到Web标准中很小的一部分。面对这样的现实我们很快就想到能否对笨重的Web容器进行功能裁剪在仅保留必要的Web标准和渲染能力的基础上使得友好的开发体验与稳定的渲染性能保持一个平衡
答案当然是可以。
泛Web容器时代的解决方案优化了Web容器时代的加载、解析和渲染这三大过程把影响它们独立运行的Web标准进行了裁剪以相对简单的方式支持了构建移动端页面必要的Web标准如Flexbox等也保证了便捷的前端开发体验同时这个时代的解决方案基本上完全放弃了浏览器控件渲染而是采用原生自带的UI组件实现代替了核心的渲染引擎仅保持必要的基本控件渲染能力从而使得渲染过程更加简化也保证了良好的渲染性能。
也就是说在泛Web容器时代我们仍然采用前端友好的JavaScript进行开发整体加载、渲染机制大大简化并且由原生接管绘制即将原生系统作为渲染的后端为依托于JavaScript虚拟机的JavaScript代码提供所需要的UI控件的实体。这也是现在绝大部分跨平台框架的思路而React Native和Weex就是其中的佼佼者。
![](https://static001.geekbang.org/resource/image/3a/0b/3af2c590f42c0b924a22bb135134380b.png)
图2 泛Web容器框架
为了追求性能体验的极致并进一步维持方案的简单可扩展性有些轻量级的跨平台方案甚至会完全抛弃Web标准、放弃JavaScript的动态执行能力而自创一套原生DSL如天猫的[VirtualView](http://pingguohe.net/2017/12/07/Tangram-2.html)框架。从广义上来说这些方案也是泛Web容器类方案。
### 自绘引擎时代
泛Web容器时代使用原生控件承载界面渲染固然解决了不少性能问题但同时也带来了新的问题。抛开框架本身需要处理大量平台相关的逻辑外随着系统版本变化和API的变化我们还需要处理不同平台的原生控件渲染能力差异修复各类奇奇怪怪的Bug。始终需要Follow Native的思维方式就使得泛Web容器框架的跨平台特性被大打折扣。
而这一时期的代表Flutter则开辟了一种全新的思路即从头到尾重写一套跨平台的UI框架包括渲染逻辑甚至是开发语言。
* 渲染引擎依靠跨平台的Skia图形库来实现Skia引擎会将使用Dart构建的抽象的视图结构数据加工成GPU数据交由OpenGL最终提供给GPU渲染至此完成渲染闭环因此可以在最大程度上保证一款应用在不同平台、不同设备上的体验一致性。
* 而开发语言选用的是同时支持JITJust-in-Time即时编译和AOTAhead-of-Time预编译的Dart不仅保证了开发效率更提升了执行效率比使用JavaScript开发的泛Web容器方案要高得多
![](https://static001.geekbang.org/resource/image/85/2c/85dfb3f8a803a0bf573f3fce63ddc92c.png)
图3 自绘引擎开发框架
通过这样的思路Flutter可以尽可能地减少不同平台之间的差异, 同时保持和原生开发一样的高性能。所以说Flutter成了三类跨平台移动开发方案中最灵活的那个也成了目前最受业界关注的框架。
现在,我们已经弄明白了三类跨平台方案,那么我在开发应用的时候,到底应该如何选择最适合自己的框架呢?
## 我该选择哪一类跨平台开发方案?
从不同的角度来看,三个时代的跨平台框架代表们在开发效率、渲染性能、维护成本和社区生态上各有优劣,如下图所示:
![](https://static001.geekbang.org/resource/image/40/3f/40a62d2a54b1814d07479dca1178fb3f.png)
图 4 主流跨平台框架对比
我们在做技术选型时可以参考以上维度从开发效率、技术栈、性能表现、维护成本和社区生态来进行综合考虑。比如是否必须支持动态化是只解决Android、iOS的跨端问题还是要包括Web对性能要求如何对多端体验的绝对一致性和维护成本是否有强诉求
从各个维度综合考量React Native和Flutter无疑是最均衡的两种跨平台开发方案而其他的方案或多或少都“偏科严重”。
* React Native依托于Facebook经过4年多的发展已经成长为跨平台开发方案的实际领导者并拥有较为丰富的第三方库和开发社区
* Flutter以挑战者姿态出现在我们的面前可以提供更彻底的跨平台技术解决方案。虽然Flutter推出时间不长但也有了诸多商用案例加上清晰的[产品路线图](https://mp.weixin.qq.com/s/aRhQdMd0R74adph9_V0wgQ)和Google的强大号召力Flutter未来的发展非常值得期待。
那么问题来了我究竟应该选择React Native还是Flutter呢
在这里,我和你说一下我的建议吧。
**对于知识学习来说,**这两个应用层面的框架最好都学。学习的过程中最重要的是打好基础深入理解框架的原理和设计思想重点思考它们的API设计的取舍发现它们的共性和差异。
Flutter作为后来者也从React Native社区学习和借鉴了不少的优秀设计很多概念两边都有对应比如React Native的Component和Flutter的Widget、Flex布局思想、状态管理和函数式编程等等这类的知识都是两个框架通用的技术。**未来也许还会出现新的解决方案,老框架也会不断更新,只有掌握核心原理才能真正立于不败之地。**
**对于实际项目来说,**这两个框架都已达到了大面积商业应用的标准。综合成熟度和生态目前俩看React Native略胜Flutter。因此如果是中短期项目的话我建议使用React Native。但作为技术选型我们要看得更远一些。Flutter的设计理念比较先进解决方案也相对彻底在渲染能力的一致性以及性能上和React Native相比优势非常明显。
此外Flutter的野心不仅仅是移动端。前段时间Google团队已经完成了Hummingbird即Flutter的Web的官方Demo在桌面操作系统的探索上也取得了进展未来大前端技术栈是否会由Flutter完成统一值得期待。
## 小结
这就是今天分享的全部内容了。
在不同平台开发和维护同一个产品所付出的成本一直以来一个令人头疼的问题于是各类跨平台开发方案顺应而生。从Web容器时代到以React Native、Weex为代表的泛Web容器时代最后再到以Flutter为代表的自绘引擎时代这些优秀的跨平台开发框架们慢慢抹平了各个平台的差异使得操作系统的边界变得越来越模糊。
与此同时这个时代对开发者的要求也到达了一个新的阶段拥抱大前端的时代已经向我们走来。在这个专栏里我会假设你有一定的前端Android、iOS或Web开发基础。比如你知道View是什么路由是什么如何实现一个基本页面布局等等。我会让希望迅速掌握Flutter开发的爱好者们通过一种比较熟悉和友好的路径去学习Flutter相关的代码和程序以及背后的原理和设计思想。
## 思考题
你有哪些跨平台开发框架的使用经历呢?
欢迎你在评论区给我留言分享你的经历和观点,我会在下一篇文章中等待你!感谢你的收听,也欢迎你把这篇文章分享给更多的朋友一起阅读。