移动端跨平台⽅案对⽐:ReactNative、weex、Flutter
跨平台⼀直是⽼⽣常谈的话题,cordova、ionic、react-native、weex、kotlin-native、flutter等跨平台框架百花齐放,颇有⼀股推倒原⽣开发者的势头。
为什么我们需要跨平台开发?本质上,跨平台开发是为了增加代码复⽤,减少开发者对多个平台适配的⼯作量,降低开发成本,提⾼业务专注的同时,提供⽐web更好的体验。
⽬前移动端跨平台开发中,备受关注的⽅案⼤致归纳为以下⼏种情况:
1)react native、weex均使⽤JavaScript作为编程语⾔,⽬前JavaScript在跨平台开发中,可谓占据半壁江⼭,⼤有“⼀统天下”的趋势;
2)kotlin-native开始⽀持 iOS 和 Web 开发,(kotlin已经成为android的⼀级语⾔)也想尝试“⼀统天下”;
3)flutter是Google跨平台移动UI框架,Dart作为⾕歌的亲⼉⼦,毫⽆疑问Dart成为flutter的编程语⾔。作为巨头新⽣⼉,在flutter官⽹也可以看出,flutter同样“⼼怀天下”(可⽀持Web端、Android端、iOS端等)。
本篇主要以react-native、weex、flutter,深⼊聊聊当前最⽕的这3种跨平台移动开发⽅案的实现原理、现状与未来。⾄于为什么只讲它们,因为对⽐ionic、phoneGap,它们更于 “naive” ( ⁻ )。看完本篇,相信你会对于当下跨平台移动开发的现状、实现原理、框架的选择等有更深⼊的理解。
1、React Native的原理与特性介绍
React Native技术关键词:
1)Facebook 出品;
2)JavaScript语⾔;
3)JSCore引擎;
4)React设计模式;
5)原⽣渲染。
1.1理念架构
“Learn once, write anywhere” ,代表着 Facebook对 react native 的定义:学习 react ,同时掌握 web
与 app 两种开发技能。 react native ⽤了 react 的设计模式,但UI渲染、动画效果、⽹络请求等均由原⽣端实现。开发者编写的js代码,通过 react native 的中间层转化为原⽣控件和操作,⽐ionic等跨平台应⽤,⼤⼤提⾼了的⽤户体验。
总结起来其实就是:React Native是利⽤ JS 来调⽤ Native 端的组件,从⽽实现相应的功能。
react native 的跨平台实现主要由三层构成,其中 C++ 实现的动态连结库(.so),作为中间适配层桥接,实现了js端与原⽣端的双向通信交互。这⾥最主要是封装了 JavaScriptCore 执⾏js的解析,⽽ react native 运⾏在JavaScriptCore中,所以不存在浏览器兼容的问题。
其中在IOS上直接使⽤内置的javascriptcore,在Android 则使⽤官⽅开源的jsc.so。
1.2、实现原理
和前端开发不同,react native 所有的标签都不是真实控件,JS代码中所写控件的作⽤,类似 Map 中的 key 值。JS端通过这个 key 组合的 Dom ,最后Native端会解析这个 Dom ,得到对应的Native控件渲染,如 Android 中<view> 标签对应 ViewGroup 控件。
在 react native 中,JS端是运⾏在独⽴的线程中(称为JS Thread )。JS Thread 作为单线程逻辑,不可能处理耗时的操作。那么如 fetch 、图⽚加载、数据持久化等操作,在 Android 中实际对应的是 o
khttp 、Fresco 、SharedPreferences等。⽽跨线程通信,也意味着 Js Thread 和原⽣之间交互与通讯是异步的。
可以看出,跨平台的关键在于C++层,开发⼈员⼤部分时候,只专注于JS 端的代码实现。在原⽣端提供的各种 Native Module 模块(如⽹络请求,ViewGroup控件),和 JS 端提供的各种 JS Module(如JS EventEmiter模块),都会在C++实现的so中保存起来,双⽅的通讯通过C++中的保存的映射,最终实现两端的交互。通信的数据和指令,在中间层会被转为String字符串传输,
1.3、打包加载
最终,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⽂件,然后启动项⽬
2、WEEX
Alibaba 出品,JavaScript语⾔,JS V8引擎,Vue设计模式,原⽣渲染
2.1、理念架构
“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.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 接收到 js ⽂件以后,JS Framework 根据⽂件为 Vue 模式,会调⽤weex-vue-framework 中提供的 createInstance⽅法创建实例。(也可能是Rax模式)
react native createInstance 中会执⾏ Js Entry 代码⾥ new Vue() 创建⼀个组件,通过其 render 函数创建出 Virtual DOM 节点。
由JS V8 引擎上解析 Virtual DOM ,得到 Json 数据发送⾄ Dom 线,这⾥输出 Json 也是⽅便跨端的数据传输。
Dom 线程解析 Json 数据,得到对应的 WxDomObject,然后创建对应的WxComponent 提交 Render 。
Render在原⽣端最终处理处理渲染任务,并回调⾥JS⽅法。
得益于上层的统⼀性,只是通过 weex-vue-framework 判断是由Vue.js ⽣成真实的 Dom ,还是通过 Native Api 渲染组件,weex ⼀定程度上上,⽤JS 实现了 vue ⼀统天下的效果。
weex 在原⽣渲染 Render 时,在接收到渲染指令后,会逐步将数据渲染成原⽣组件。Render 通过解析渲染数据的描述,然后分发给不同的模块。
⽐如控件渲染属于 dom 模块中,页⾯跳转属于navigator模块等。模块的渲染过程并⾮⼀个执⾏完,再执⾏另⼀个的流程,⽽是类似流式的过程。如上⼀个 <text> 的组件还没渲染好,下⼀个 <div> 的渲染⼜发了过来。这样当⼀个组件的嵌套组件很多时,或者可以看到这个⼤组件内的UI,⼀个⼀个渲染出来的过程。
weex ⽐起react native,主要是在JS V8的引擎上,多了 JS Framework 承当了重要的职责,使得上层具备统⼀性,可以⽀持跨三个平台。总的来说它主要负责是:管理Weex的⽣命周期;解析JS Bundle,转为Virtual DOM,再通过所在平台不同的API⽅法,构建页⾯;进⾏双向的数据交互和响应。
2.3、打包
weex 作为 react-native 之后出现的跨平台实现⽅案,⾃然可以站在前⼈的肩膀上优化问题,⽐如:Bundle⽂件过⼤问题。
Bundle⽂件的⼤⼩,很⼤程度上影响了框架的性能,⽽ weex 选择将 JS Framework 集成到 WeexSDK 中,⼀定程度减少了JS Bundle的体积,使得 bundle ⾥⾯只保留业务代码。
打包时,weex 是通过 webpack 打包出 bundle ⽂件的。bundle ⽂件的打包和 entry.js ⽂件的配置数量有关,默认情况下之后⼀个 entry ⽂件,⾃然也就只有⼀个bundle⽂件。
在 weex 项⽬的 f.js 中可以看到,其实打包也是区分了 webConfig 和 weexConfig 的不同打包⽅式。如下图,其中weexEntry 就是 weex 打包配置的地⽅,可以看到本来已经有 index 和 entry.js 存在了。如果有需要,可配置上你需要的打包页⾯,具体这⾥就不详细展开了。有兴趣可看:。
3、Flutter
Google 出品,Dart语⾔,Flutter Engine引擎,响应式设计模式,原⽣渲染
Flutter 是⾕歌2018年发布的跨平台移动UI框架。相较于本⼈已经在项⽬中使⽤过 react native 和 Weex,Flutter⽬前仅仅是简单运⾏过Demo,毕竟还是beta 阶段,这⾥更多的聊⼀下它的实现机制和效果。
与 react native 和 weex 的通过 Javascript 开发不同,Flutter 的编程语⾔是Drat,(⾕歌亲⼉⼦,据说是因为 Drat 项⽬组就在 Flutter 隔壁⽽被选上(◐‿◑))所以执⾏时并不需要 Javascript 引擎,但实际效果最终也通过原⽣渲染。
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⼩很多。
三、对⽐
这算是互相伤害的环节了吧。(///▽///)
类型React Native Weex Flutter
平台实现JavaScript JavaScript⽆桥接,原⽣编码
引擎JS V8JSCore Flutter engine
核⼼语⾔React Vue Dart
Apk⼤⼩ (Release)7.6M10.6M8.1M
bundle⽂件⼤⼩默认单⼀、较⼤较⼩、多页⾯可多⽂件不需要
上⼿难度稍⾼?容易⼀般
框架程度较重较轻重
特点(不局限)适合开发整体App适合单页⾯适合开发整体App
社区丰富,Facebook重点维护有点残念,托管apache刚刚出道⼩鲜⾁,拥护者众多
⽀持Android、IOS Android、IOS、Web Android、IOS(并不⽌?)
1、⼤⼩
上⾯Apk⼤⼩是通过 react-native init、weex create 和 flutter 创建出的⼯程后,直接不添加任何代码,打包出来的 release 签名 apk ⼤⼩。从下图可以看出,其中⼤⽐例都是在so库。
2、社
react native 作为 Facebook 主⼒开源项⽬之⼀,⾄今已有各类丰富的第三⽅库,甚⾄如 realm、lottie 等开源项⽬也有 react native 相关的版本,社实际⽆需质疑。当然,因为并完全正统开发平台,第三库的健壮性和兼容性有时候总是良莠不齐。
weex 其实有种⽣错在国内的感觉。其实 weex 的设计和理念都很优秀,性能也不错,但是对⽐ react native 的第三⽅⽀持,就显得有点后妈养的。2016年开源⾄今,社区和各类⽂档都显得有点疲弱,作为跨平台开发⼈员,⼤多时候肯定不会希望,需要频繁的⾃⼰增加原⽣功能⽀持,因为这样的⼯作⼀多,反⽽会与跨平台开发的理念背道⽽驰,带来开发成本被维护难度增加。
Flutter⽬前还处理beta阶段,但是⾕歌的号召⼒⼀直很可观,这⼀点⽆需质疑。
3、性能
理论上 flutter 的性能应该是最好的,但是⽬前实际体验中,却并没有感受出来太⼤的差距,和 react native(0.5.0之后)、weex 在性能上个⼈体验差异不是很⼤。当然,这⾥并没有实测渲染的毫秒时间和帧率数据。
4、其他区别
Weex的多页⾯实现问题
weex 在 native 端是不⽀持 <keep-alive> 的,这⼀点和 react-native 不同在与,如果在 native 需要实现页⾯跳转,使⽤ vue-router 将会惨不忍睹:返回后页⾯不做特别处理时,是会空⽩⼀⽚。参考官⽅Demo ,native 端的采⽤ quireModule('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代码中也会添加跟踪修改。
四、未来趋势
我们选择框架的时候,很多时候会关注框架的成熟度和⽣命⼒不是么(◐‿◑)。
1、React Native
: Airbnb 作为 react native 平台上最⼤的⽀持者之⼀,其开源的lottie 同样是⽀持原⽣和 react native。
Airbnb 在宣布放弃的⽂中,也对 react native 的表⽰了很⼤量的肯定,⽽使得他们放弃的理由,其实主要还是集中于项⽬庞⼤之后的维护困难,第三⽅库的良莠不齐,兼容上需要耗费更多的精⼒导致放弃。
ps:( Lottie库Airbnb出的是⼀个能够帮助解析AE导出的包含动画信息的json⽂件。这很好的解决了⼀个⽭盾,设计师可以更专注的设计出各种炫酷的动画效果,⽽开发只需要将其加⼊⽀持即可。)
在经历了开源协议风波后,可以看出 Facebook 对于 react native 还是很看重的,这些底层重构优化的地⽅,主要集中于:
⾸先,改变线程模型。UI 更新不再需要在三个不同的线程上执⾏,⽽是可以在任意线程上同步调⽤ JavaScript 进⾏优先更新,同时将低优先级⼯作推出主线程,以便保持对 UI 的响应。
其次,将异步渲染功能引⼊ React Native 中,允许执⾏多个渲染并简化异步数据处理。
最后,简化桥接,让它更快、更轻量。原⽣和 JavaScript 之间的直接调⽤效率更⾼,并且可以更轻松地构建调试⼯具,如跨语⾔堆栈跟踪。
2、Weex
2018年初的新闻可以看出,weex 的遭遇有点类似曾经的 Duddo(Dubbo因为内部竞争被阿⾥⼀度放弃维护),这波诈⼫后 weex 被托管到了Apache,⽽github的如今也还保持着更新,希望后续能有多好的发展,拭⽬以待吧。
3、Flutter
Flutter 是 Google 跨平台移动UI框架,Dart作为⾕歌的亲⼉⼦在 Flutter 中使⽤,并且⾕歌新操作系统 Fuchsia ⽀持 Dart,使⽤ Flutter 作为操作UI框架。这些集合到⼀起难免让你怀疑 Android 是否要被⾕歌抛弃的想法。
或者如今先 Android 等平台上推⼴ Flutter 与 Dart,就是为了以后跟好的过渡到新系统上,毕竟开发者是操作系统的⽣命源泉之⼀。⽽ Java 与 JVM 或者可以被⾕歌完全抛开。当然,⽬前看起来 Flutter 貌似还缺少⼀些语法糖,嵌套下来的代码有点不忍直视,或者到正式版之后,我们更能感受出它的美丽吧。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论