跨平台一直是老生常谈的话题,cordova、ionic、react-native、weex、kotlin-native、flutter等跨平台框架的百花齐放,很有一股推倒原生开发者的势头。本文将对当下跨平台移动开发的现状、实现原理、框架的选择等进行深度解析。前端
为何咱们须要跨平台开发? 本质上,跨平台开发是为了增长代码复用,减小开发者对多个平台差别适配的工做量,下降开发成本,提升业务专一的同时,提供比web更好的体验。通俗了说就是:省钱、偷懒。vue
本篇主要以react-native、weex、flutter,结合资讯展望,深刻聊聊当前跨平台移动开发的实现原理、现状与将来。至于为何只讲它们,由于对比ionic、phoneGap,它们更于 “naive”。java
1、原理与特性node
目前移动端跨平台开发中,大体概括为如下几种状况:react
一、React Nativeandroid
Facebook 出品,Java语言,JSCore引擎,React设计模式,原生渲染webpack
1.一、理念架构ios
“Learn once, write anywhere”,表明着 Facebook对 react native 的定义:学习 react ,同时掌握web与app两种开发技能。 react native 用了 react 的设计模式,但UI渲染、动画效果、网络请求等均由原生端实现。开发者编写的js代码,经过 react native 的中间层转化为原生控件和操做,比ionic等跨平台应用,大大提升了的用户体验。git
总结起来其实就是利用 JS 来调用 Native 端的组件,从而实现相应的功能。github
以下图所示,react native 的跨平台是实现主要由三层构成,其中 C++ 实现的动态连结库(.so),做为中间适配层桥接,实现了js端与原生端的双向通讯交互。这里最主要是封装了JavaCore执行js的解析,而 react native 运行在JavaCore中,因此不存在浏览器兼容的问题。
其中在IOS上直接使用内置的javacore, 在Android 则使用webkit.org官方开源的jsc.so。
1.二、实现原理
和前端开发不一样,react native 全部的标签都不是真实控件,JS代码中所写控件的做用,相似 Map 中的 key 值。JS端经过这个 key 组合的 Dom ,最后Native端会解析这个 Dom ,获得对应的Native控件渲染,如 Android 中<view>标签对应ViewGroup 控件。
在 react native 中,JS端是运行在独立的线程中(称为JS Thread )。JS Thread 做为单线程逻辑,不可能处理耗时的操做。那么如fetch 、图片加载、数据持久化等操做,在 Android 中实际对应的是okhttp 、Fresco 、SharedPreferences等。而跨线程通讯,也意味着 Js Thread 和原生之间交互与通信是异步的。
能够看出,跨平台的关键在于C++层,开发人员大部分时候,只专一于JS 端的代码实现。 在原生端提供的各类 Native Module 模块(如网络请求,ViewGroup控件),和 JS 端提供的各类 JS Module(如JS EventEmiter模块),都会在C++实现的so中保存起来,双方的通信经过C++中的保存的映射,最终实现两端的交互。通讯的数据和指令,在中间层会被转为String字符串传输,双向的调用流程以下图。
1.三、打包加载
最终,JS代码会被打包成一个 bundle 文件,自动添加到 App 的资源目录下。react native 的打包脚本目录为/node_modules/react-native/local-cli,打包最后会经过 metro 模块压缩 bundle 文件。而bundle文件只会打包js代码,天然不会包含图片等静态资源,因此打包后的静态资源,实际上是被拷贝到对应的平台资源文件夹中。
其中图片等存在资源的映射规则,好比放在 react native 项目根目录下的 img/pic/logo.png 的资源,编译时,会被重命名后,根据大小 merged 到对应的是drawable目录下,修更名称为img_pic_logo.png。
打包Android和IOS,确定须要相应的平台项目存在,在 react-native init 时建立的项目,就已经包含了 android 和 ios 的模版工程,打包完的工程会加载bundle文件,而后启动项目,以下图。这里就不展(tou)开(lan)了,有兴趣的能够看:React Native For Android 架构初探 。
二、WEEX
Alibaba 出品,Java语言,JS V8引擎,Vue设计模式,原生渲染
2.一、理念架构
“Write once, run everywhere”, weex的定义就像是:写个 vue 前端,顺便帮你编译成性能还不错的 apk 和 ipa(固然,现实有时很骨感)。基于 Vue 设计模式,支持 web、android、ios 三端,原生端一样经过中间层转化,将控件和操做转化为原生逻辑来提升用户体验。
在 weex 中,主要包括三大部分:JS Bridge、Render、Dom,分别对应WXBridgeManager、WXRenderManager、WXDomManager,三部分经过WXSDKManager统一管理。其中 JS Bridge 和 Dom 都运行在独立的 HandlerThread 中,而 Render 运行在 UI 线程。
JS Bridge 主要用来和 JS 端实现进行双向通讯,好比把 JS 端的 dom 结构传递给 Dom 线程。Dom 主要是用于负责 dom 的解析、映射、添加等等的操做,最后通知UI线程更新。而 Render 负责在UI线程中对 dom 实现渲染。
2.二、实现原理
和 react native同样,weex 全部的标签也不是真实控件,JS 代码中所生成存的 dom,最后都是由 Native 端解析,再获得对应的Native控件渲染,如 Android 中<text>标签对应WXTextView控件。
weex 中文件默认为 .vue ,而 vue 文件是被没法直接运行的,因此 vue 会被编译成 .js 格式的文件,Weex SDK会负责加载渲染这个js文件。Weex能够作到跨三端的原理在于:在开发过程当中,代码模式、编译过程、模板组件、数据绑定、生命周期等上层语法是一致的。不一样的是在JS Framework层的最后,web 平台和 Native 平台,对 Virtual DOM 执行的解析方法是有区别的。
实际上,在 Native 中对 bundle 文件的加载大体经历如下阶段:
得益于上层的统一性,只是经过weex-vue-framework判断是由Vue.js生成真实的 Dom ,仍是经过 Native Api 渲染组件,weex 必定程度上上,用JS 实现了vue一统天下的效果。
weex 在原生渲染 Render 时,在接收到渲染指令后,会逐步将数据渲染成原生组件。Render 经过解析渲染数据的描述,而后分发给不一样的模块。
好比 控件渲染属于dom模块中,页面跳转属于navigator模块等。模块的渲染过程并不是一个执行完,再执行另外一个的流程,而是相似流式的过程。如上一个<text>的组件还没渲染好,下一个<div>的渲染又发了过来。这样当一个组件的嵌套组件不少时,或者能够看到这个大组件内的UI,一个一个渲染出来的过程。
承当了重要的职责,使得上层具有统一性,能够支持跨三个平台。总的来讲它主要负责是:管理Weex的生命周期;解析JS Bundle,转为Virtual DOM,再经过所在平台不一样的API方法,构建页面;进行双向的数据交互和响应。
2.三、打包
weex 做为 react-native 以后出现的跨平台实现方案,天然能够站在前人的肩膀上优化问题,好比:Bundle文件过大问题。
Bundle文件的大小,很大程度上影响了框架的性能,而 weex 选择将JS Framework集成到 WeexSDK 中,必定程度减小了JS Bundle的体积,使得 bundle 里面只保留业务代码。
打包时,weex 是经过 webpack 打包出 bundle 文件的。bundle 文件的打包和 entry.js文件的配置数量有关,默认状况下以后一个 entry 文件,天然也就只有一个bundle文件。
在 weex 项目的 webpack.common.conf.js中能够看到,其实打包也是区分了 webConfig和weexConfig的不一样打包方式。以下图,其中weexEntry 就是 weex 打包配置的地方,能够看到原本已经有index和 entry.js存在了。若是有须要,可配置上你须要的打包页面,具体这里就不详细展开了。有兴趣可看:Weex原理之带你去蹲坑 。
三、Flutter
Google 出品,Dart语言,Flutter Engine引擎,响应式设计模式,原生渲染
Flutter 是谷歌2018年发布的跨平台移动UI框架。相较于本人已经在项目中使用过 react native 和 Weex,Flutter目前仅仅是简单运行过Demo,毕竟仍是beta 阶段,这里更多的聊一下它的实现机制和效果。
与 react native 和 weex 的经过 Java 开发不一样,Flutter 的编程语言是Drat,(谷歌亲儿子,听说是由于 Drat 项目组就在 Flutter 隔壁而被选上( ))因此执行时并不须要 Java 引擎,但实际效果最终也经过原生渲染。
如上图,Flutter 主要分为Framework和Engine,咱们基于Framework 开发App,运行在 Engine 上。Engine 是 Flutter 的独立虚拟机,由它适配和提供跨平台支持,目前猜想 Flutter 应用程序在 Android 上,是直接运行 Engine 上 因此在是不须要Dalvik虚拟机。(这是比kotlin更完全,抛弃JVM的纠缠?)
以下图,得益于 Engine 层,Flutter 甚至不使用移动平台的原生控件, 而是使用本身 Engine 来绘制 Widget (Flutter的显示单元),而 Dart 代码都是经过 AOT 编译为平台的原生代码,因此 Flutter 能够 直接与平台通讯,不须要JS引擎的桥接。同时 Flutter 惟一要求系统提供的是 canvas,以实现UI的绘制。咦?这么想来,支持web端也没问题吧!
在Flutter中,大多数东西都是widget,而widget是不可变的,仅支持一帧,而且在每一帧上不会直接更新,要更新而必须使用Widget的状态。无状态和有状态 widget 的核心特性是相同的,每一帧它们都会从新构建,有一个State对象,它能够跨帧存储状态数据并恢复它。
Flutter 上 Android 自带了 Skia,Skia是一个 2D的绘图引擎库,跨平台,因此能够被嵌入到 Flutter的 iOS SDK中,也使得 Flutter Android SDK要比 iOS SDK小不少。
2、对比
这算是互相伤害的环节了吧。
一、大小
上面Apk大小是经过react-native init、weex create 和 flutter 建立出的工程后,直接不添加任何代码,打包出来的 release 签名 apk 大小。从下图能够看出,其中大比例都是在so库。
二、社群
react native 做为 Facebook 主力开源项目之一,至今已有各种丰富的第三方库,甚至如realm、lottie 等开源项目也有 react native 相关的版本,社群实际无需质疑。固然,由于并彻底正统开发平台,第三库的健壮性和兼容性有时候老是参差不齐。
weex 其实有种生错在国内的感受。其实 weex 的设计和理念都很优秀,性能也不错,可是对比 react native 的第三方支持,就显得有点后妈养的。2016年开源至今,社区和各种文档都显得有点疲弱,做为跨平台开发人员,大多时候确定不会但愿,须要频繁的本身增长原生功能支持,由于这样的工做一多,反而会与跨平台开发的理念背道而驰,带来开发成本被维护难度增长。
Flutter目前还处理beta阶段,可是谷歌的号召力一直很可观,这一点无需质疑。
三、性能
理论上 flutter 的性能应该是最好的,可是目前实际体验中,却并无感觉出来太大的差距,和 react native(0.5.0以后)、weex 在性能上我的体验差别不是很大。固然,这里并无实测渲染的毫秒时间和帧率数据。
四、其余区别
Weex的多页面实现问题
weex 在 native 端是不支持<keep-alive> 的,这一点和 react-native 不一样在与,若是在 native 须要实现页面跳转,使用 vue-router 将会惨不忍睹:返回后页面不作特别处理时,是会空白一片。参考官方Demo playground ,native 端 的采用weex.requireModule('navigator')跳转 Activity 是才正确实现。
同时,weex中 navigator 跳转的设计,也致使了多页面的页面间通信的差别。weex在多页面下的数据通信,是经过url实现的,好比file://assets/dist/SecondPage.js params=0,而vuex和vue-router在跨页面是没法共用的;而 react native 在跨 Actvity 使用时,由于是同一个bundle文件,只要 manager 相同,那么 router 和 store 时能够照样使用的,数据通讯方式也和当个 Actvity 没区别。
项目模板
weex 和 react native 模板代码模式也不一样。weex 的模板是从 cordova 模式修改过来的,根据platform需求,用命令添加固定模块,而在 .gitignore 对 platforms文件夹是忽略跟踪。 react native 在项目建立时模版就存在了,特别是添加第三方插件原生端支持时,会直接修改模板代码,git代码中也会添加跟踪修改。
3、将来趋势
咱们选择框架的时候,不少时候会关注框架的成熟度和生命力不是么。
一、React Native
“Airbnb 宣布放弃使用 React Native,回归使用原生技术” : Airbnb 做为 react native 平台上最大的支持者之一,其开源的lottie一样是支持原生和 react native。
Airbnb 在宣布放弃的文中,也对 react native 的表示了很大量的确定,而使得他们放弃的理由,其实主要仍是集中于项目庞大以后的维护困难,第三方库的参差不齐,兼容上须要耗费更多的精力致使放弃。
ps:( Lottie库Airbnb出的是一个可以帮助解析AE导出的包含动画信息的json文件。这很好的解决了一个矛盾,设计师能够更专一的设计出各类炫酷的动画效果,而开发只须要将其加入支持便可。)
Facebook 正在重构 React Native,将重写大量底层。 在经历了开源协议风波后,能够看出 Facebook 对于 react native 仍是很看重的, 这些底层重构优化的地方,主要集中于:
首先,改变线程模型。UI 更新再也不须要在三个不一样的线程上执行,而是能够在任意线程上同步调用 Java 进行优先更新,同时将低优先级工做推出主线程,以便保持对 UI 的响应。其次,将异步渲染功能引入 React Native 中,容许执行多个渲染并简化异步数据处理。最后,简化桥接,让它更快、更轻量。原生和 Java 之间的直接调用效率更高,而且能够更轻松地构建调试工具,如跨语言堆栈跟踪。
二、Weex
没有死!阿里公开Weex技术架构,还开源了一大波组件。 从 2018年初的新闻能够看出,weex 的遭遇有点相似曾经的 Duddo(Dubbo由于内部竞争被阿里一度放弃维护),这波诈尸后weex被托管到了Apache,而github的weexteam 现在也还保持着更新,但愿后续能有多好的发展,拭目以待吧。
三、Flutter
Flutter 是 Google 跨平台移动UI框架,Dart做为谷歌的亲儿子在 Flutter 中使用,而且谷歌新操做系统 Fuchsia 支持 Dart,使用 Flutter 做为操做UI框架。这些集合到一块儿不免让你怀疑 Android 是否要被谷歌抛弃的想法。
或者现在先 Android 等平台上推广 Flutter 与 Dart,就是为了之后跟好的过渡到新系统上,毕竟开发者是操做系统的生命源泉之一。而 Java 与 JVM 或者能够被谷歌彻底抛开。固然,目前看起来 Flutter 貌似还缺乏一些语法糖,嵌套下来的代码有点不忍直视,或者到正式版以后,咱们更能感觉出它的美丽吧。
本文做者恋猫月亮