过去十数年间,跨平台开发技术仍然是开发者们探求的方向,"WriteOnce,RunEverywhere"是无数开发者追逐的目标。放眼今朝,应用运行的平台不但没有降低,反倒呈愈发碎片化下降的趋势。开发者不仅须要考虑兼容Web、iOS、Android平台外,还须要考虑兼容各家小程序乃至各类IOT设备等。
不难看出,能减少开发与维护成本,让业务快速落地的跨平台开发技术将在未来很长一段时间里持续发光发热。本文首先将带你们回顾业界内主要的跨平台技术的发展历史和迭代趋势,并介绍公司内部针对不同业务场景所诞生的各类跨平台容器技术,最后会探讨Taro作为一款跨平台开发框架对“一码多端”的思索与实践。
跨平台技术发展史跨平台技术分类
按技术本质分类,业界的跨平台技术可以按渲染管道的不同来大致界定为以下三类:
Hybrid
#4:8:b:6:4:d:7:6:7:3:9:9:d:a:b:3:f:3:e:0:6:a:a:4:4:8:6:6:9:1:9:e#
Hybrid是最初期的跨平台技术,一方面基于WebView让开发者可以快速开发跨平台应用,另一方面WebView的性能未能与原生开发相比,尤其在10年前那种手机硬件配置的背景下,当时跨平台意味着糟糕的体验。
随着手机硬件的提高和Hybrid技术的发展,通过预载、缓存、同层渲染等手段,性能与体验早已有相当大的提高。似乎在个别交互复杂的场景一直未能与原生相比,但是开发简单、生态丰富等特性让它在好多场景都有一战之力,因而未来Hybrid仍会是主流的跨平台技术之一。
在此也简单提一下小程序。小程序是带着浓厚商业气息的跨平台方案,为了隔离渲染环境,界定了在JS引擎执行的逻辑层和在WebView渲染的渲染层,本质还是基于WebView渲染的Hybrid技术。同时它也使用了好多手段优化性能与体验,如预载、每个页面对应一个WebView等,总体来说也有着不错的跨平台疗效。
ReactNative
2015年Facebook推出了ReactNative,它迅速吸引了大批开发者,逐渐成为一大主流的跨平台技术。开发者使用JS进行开发,RN保持着React虚拟DOM节点和NativeUI控件的映射关系,每次渲染都通过Bridge传递虚拟DOM树的数据来调用原生控件进行渲染,因而甩掉了WebView繁杂的渲染管道,拥有比Hybrid更佳的布局和渲染性能。
Bridge交互的双方(JS和Native)没有共享显存,靠JSON序列化数据进行通讯,同时Bridge限制调用频度、只容许异步调用等特征对UI渲染来说是致命的。在滑动、动画、手势操作等更新频度较高的场景里,低效Bridge通讯未能让JS更新及时反应在UI上,常常会出现黑屏或卡顿现象。另外,RN没有完全屏蔽原生平台,对iOS和Android的实现细节还有所依赖,开发者须要对Native有一定的了解才会写好RN。
针对性能问题,RN新版本对渲染构架进行了重塑,使用JSI、Fabric、TurboModules等概念以降低对Bridge的依赖,支持同步渲染等能力。能够获得社区和商业公司的的认可还需待时间验证。
Flutter
#7:0:0:d:f:a:6:7:b:4:9:1:3:8:e:c:c:9:6:4:8:a:8:9:5:3:4:2:6:5:4:6#
2018年Google即将发布Flutter,是为Fuchsia操作系统的配套的开发方法。与Hybrid和RN不同,Flutter没有选择使用JS,而是选择了Dart作为开发语言。生产环境中Dart通过AOT编译成对应平台的指令,执行效率远远低于JS,也不须要Bridge通讯,性能比RN更好且接近原生。同时Flutter基于跨平台的Skia图形库自建了渲染引擎,最大程度地保证了跨平台渲染的一致性。
从选用Dart作为开发语言可以看出,Flutter首要目标是Android和iOS平台的统一。相比Hybrid和RN开发者会有更高的上手学习成本,同时生态相对短缺。据悉,不支持动态化更新也是Flutter的痛点之一,尽管社区有探求出一些方案,但相对地会增加Flutter的性能,对此只能见仁见智了。
不过Flutter的野心除了在统一Android和iOS,还包括Web、桌面端和嵌入式设备,相信随着Flutter核心的迭代和社区生态的发展,Flutter仍会在跨平台技术里占主要地位。
跨平台技术发展趋势
目前跨平台技术领域一直是百花齐放的状态,以开发效率、性能、一致性是对跨平台技术为主要评价标准,则各技术流派是对此二者间抉择的艺术。Hybrid拥有很高的开发效率和一致性,但是存在性能问题。RN为了解决WebView的性能问题选择了桥接原生渲染,但却放弃了CSS3等Web特点,减少了开发效率。Flutter使用Dart和自建渲染挺好地解决了性能和一致性问题,但同时也减少了开发效率。
其实,业界不仅以上三种主流的跨平台技术外,还有好多的一些探求。比如类似RN但同时支持React和Vue的Hippy;使用JS开发,基于Flutter渲染的Weex2.0;使用JS开发,也支持大部份CSS标准,基于Flutter渲染的Karken等。总体来说,跨端技术在往兼容Web开发方法(提升开发效率)和自建渲染管道(增强性能和一致性)的方向发展演变。
在易迅App里也存在着不同的跨平台技术方案,它们分别面向着不同特性的业务场景。在类似易迅的超级App里怎样统一这种技术方案的开发范式,让业务组件、通用逻辑在不同方案中复用上去是我们当前面对的一大困局。我们可以把以上这种技术统一也称跨平台容器技术。在跨平台容器之上,架设跨平台框架,统一开发句型、组件和API等规范,真正实现一码多端。
易迅跨平台技术现况
近些年来,易迅主站APP的业务形态呈现越来越多元化,而为了应对不同的业务形态诉求,主站APP内种也是存在多种跨平台技术。
JDHybrid
关键词:灵活性、时效性。
JDHybrid是一个联通端高性能Hybrid容器框架,旨在于提高H5在易迅APP内的加载与渲染性能,在电商场景下,我们一般会应对大量的营销活动场景,诸如618、双11等大促活动,为了保证活动会场的灵活性与时效性,H5是目前的最优选择,而JDHybrid方案的出现则是为H5带来了性能及体验的保障,详见Hybrid。
#f:6:7:2:9:4:9:8:9:8:2:6:6:c:9:3:e:d:e:e:a:1:0:e:b:7:c:c:3:d:8:8#
而在开发方法JDHybrid与普通H5应用基本无异,对Web后端开发十分友好。
易迅小程序
关键词:连接线上线下、独立。
易迅小程序是易迅官方推出的小程序平台,为开发者提供一种快速开发方法,连接线上线下购物能力,帮助店家、开发者以全新的方法联接消费者。易迅小程序是一种全新的开放模式,在手机易迅APP上使用,可以被方便地获取和传播,为终端用户提供更好的使用体验。同时,易迅小程序也可以作为独立引擎赋能给其他APP及终端屏,实现更广的跨端统一。
#0:3:4:4:9:0:a:a:f:4:d:a:d:f:a:c:4:f:e:3:0:0:4:4:f:6:9:7:1:b:b:4#
在开发方法上,易迅小程序与业界主流小程序方案基本一致,采用类Web的开发方法,对Web后端开发以及熟悉小程序的开发来说基本没有上手难度。
JDReact/JDFlutter
关键词:体验,效率。
JDReact/JDFlutter是易迅内部基于开源社区的ReactNative/Flutter方案,针对内部业务诉求改建而至的方案,在ReactNative、Flutter上进行了深度二次开发和功能扩充,除了打通了Android/iOS/Web三端平台,并且对易迅联通端基础业务能力进行了SDK级别的封装,提供了统一、易于开发的API。
从开发效率上来说JDReact拥有接近Web的开发体验和效率,而JDFlutter对于熟悉Dart及Flutter的人来说开发体验和效率也很高,不过由于使用Dart开发的缘故,对于Web后端开发来说JDFlutter还是有一定的上手难度;从性能上来说,得益于两者的实现机制,JDReact/JDFlutter开发应用的理论性能要强于H5及小程序(基于WebView),更接近原生性能。所以,JDReact/JDFlutter会更适宜对性能有一定要求,而对时效性没有过多要求的业务。
MCube
关键词:稳定性、性能。
在易迅APP里,消费者购物的关键环节包括搜索、商品详情页、购物车、结算下单到订单等,在整个购物链路中属于价值特别高的部份,因而被称为黄金流程,黄流对稳定性与性能的要求十分高,而同时为了兼具研制效率以及动态化诉求,所以诞生出一套原生动态化方案MCube,它是一个提供完整的跨端原生页面动态展示的解决方案,致使业务可以基于MCube做到一次开发,随时上线,多端复用的疗效。(更多可以详见Mcube介绍文章)
#b:e:6:f:c:9:2:5:a:0:9:9:b:5:9:c:4:7:7:4:c:2:9:a:0:a:b:1:3:e:2:8#
在开发方法上,Mcube采用XMLDSL形式进行开发,这对于Web后端开发来说会有一定的学习成本。
#d:a:e:f:f:6:7:1:7:3:2:4:4:a:8:5:7:1:4:4:c:0:e:0:0:b:b:c:9:b:6:f#
综上,我们易迅内部为了应对不同的业务诉求,存在多跨平台技术共存的情况,这从某种程度上来说是合理的,而且多个跨平台技术共存对我们的研制效率和学习成本上都引起了不小的影响,所以我们须要一个跨平台开发框架来实现对众多跨平台技术的研制模式统一。
Taro做了哪些
在业界有好多面向跨端需求的解决方案,各个方案之间实现的原理各有不同,也是面向不同痛点的开发者需求研制的。在这其中Taro是目前支持平台最多,也是最为开放的跨端解决方案。在当前业界中,Taro的流行程度和社区生态方面都十分领先,从学习成本、核心能力、可拓展性角度来看,Taro具备一定的优势和先进性,是大多数开发者的不二之选。
支持编译小程序平台
小程序是彰显Taro核心优势的一个方面,在所有小程序平台中,也许是通过社区开发者自发的贡献,亦或是小程序平台官方提供的端平台插件,大众开发者熟悉或不熟悉的小程序平台,都还能通过Taro得到支持。同时针对开发者的个性化需求,在Taro社区生态中,也提供各开发场景所需的插件供开发者选装,架构最适宜项目的开发工作流。
“编译时”or“运行时”
选择“编译时”还是“运行时”是好多跨端技术解决方案都须要面对的问题,在小程序跨端技术被业界关注之初,为了减少运行时性能的耗损,你们都不约而同的选择了通过在编译时基于AST作曲法句型剖析,完成代码能力的转换。对于Taro来说,在3.x先前的版本选择了“编译时”,通过这样的构架也能为开发者提供不错的开发体验。
#5:8:e:c:8:0:9:9:f:e:b:f:a:2:a:c:2:2:9:3:5:5:f:d:2:0:e:6:f:2:e:c#
并且随着框架的不断迭代,这类方案须要面对的问题渐渐显露,句型限制问题牵涉开发者心智促使框架的学习门槛降低,研制也须要付出额外的成本;同时随着写法的多样性不断提高,框架维护的难度也在不断飙升,极大的阻挠了新人贡献者参与到社区的生态当中;小程序平台的下降也成为不得不考虑的重要诱因,构架早已不足以支撑框架大步往前迈向……这些问题都促使Taro框架不断往前,构架也渐渐从“编译时”走向“运行时”。
框架层面的设计理念改变,给Taro社区生态注入了更多的活力。而对开发者来说,在小程序中提供完整的React支持,才能急剧提高开发体验,同时也极大的增加了框架的学习门槛。
#3:9:9:d:3:e:f:6:2:4:5:9:a:8:1:c:7:a:a:d:6:9:a:f:8:6:f:5:2:d:a:e#
全新的构架在帮助开发者更好的支持React的同时,也提供了一个抓手支持更多的后端框架,例如对于Vue、Preact等等框架来说,同样有着庞大的开发者受众,为她们提供跨端技术的支持才能为Taro提供更多的业务和研制场景。在摒弃框架层面DSL限制的同时,Taro也不再限制开发者使用的语言,理论上我们支持任何可以最终编译到JavaScript的语言重构Taro项目,例如TypeScript、CoffeeScript等等。
插件化能力支持
Taro对于自己的定位是一个「开放式跨端跨框架解决方案」,在支持多端统一开发的特点同时,更重要的是成为一个开放式的解决方案,所以我们希望通过支持插件化能力为Taro拓展更多平台提供基础支持,也才能将我们现有的能力前馈,为支持更多的平台和能力发展提供框架层面的基础支持。
在当前的框架构架中,插件大致分为三种类型:框架插件、端平台插件、其它能力插件,其中框架类型用于实现对于React、Vue等后端框架的支持;端平台插件则提供了编译到平台或则端的能力支持,小程序端平台通过插件纵向或横向拓展,很大程度上节约了维护成本;而其它能力插件则通过Taro内核曝露下来的编译时、运行时的生命周期,为Taro生态提供有力的支持。
#2:9:b:6:1:4:f:d:8:2:9:9:7:7:5:3:e:0:a:8:4:b:a:9:1:f:5:4:f:3:3:b#
插件化支持,为Taro添上了好多想像的空间,我们希望开发者可以依据Taro提供的API开发一个插件能够实现自己去为Taro扩充更多平台与后端框架的支持,比如未来有些新的平台推出小程序,或则有人希望能在Taro中使用Angular等更多的后端框架,这么就可以通过Taro的开放式机制来自行扩充,而不用等待Taro官方来进行支持,Taro将只作为一个跨端适配的平台,所有的可能性都可以让社区自己去自由开掘。
性能体验优化
运行时性能主要分为两个部份,一是更新性能,二是初始化性能。
初始化性能则是一个不小的痛点。原生小程序或编译型框架的初始数据可以直接用于渲染,但Taro在初始化时会把框架的渲染数据转化为小程序的渲染数据,多了一次setData开支。为了解决这个问题,Taro从服务端渲染遭到启发,在TaroCLI将页面初始化的状态直接渲染为无状态的wxml,在框架和业务逻辑运行之前执行渲染流程。我们将这一技术称之为预渲染(Prerender),经过Prerender的页面初始渲染速率一般会和原生小程序一致甚至更快。
对于更新性能而言,Taro将diff的工作交给了开发者使用的框架(React/Nerv/Vue),而框架diff以后的数据也会通过Taro按路径去最小化更新。为此开发者可以依照使用框架的特点进行更多更细微的性能优化。但若果页面结构比较复杂,应用更新的性能都会增长。因此我们引入了一个基础组件CustomWrapper,它的作用是创建一个原生自定义组件。对后代节点的setData将由此自定义组件进行调用,达到局部更新的疗效,因而提高更新性能。
开发者可以使用CustomWrapper去包裹遇见更新性能问题的组件:
//...
除此以外,Taro还为更多开发者遇见个各类情况提供了标准的解决方案,例如专为解决长列表渲染问题提供的虚拟列表组件,还有解决超大项目编译效率问题的依赖预编译特点等等,可以抵达Taro官网了解更多哦。
支持编译Web端应用
框架层面支持一个类小程序的Web应用,须要做的事情大致可以归纳为三类:路由、标准组件库、标准API。在Taro中也是这么,我们通过这三个方面的工作对齐不同框架的组件和生命周期,为Taro项目提供Web端编译的能力,让开发者可以在开死党程序的同时,建立项目的Web版本。
#d:f:e:c:4:6:9:c:4:1:a:4:a:c:5:5:2:2:9:c:a:3:e:0:c:e:a:a:c:2:2:a#
Web端生态
Web端场景和小程序中会有很大的不同,好多时侯我们在设计能力时都须要平衡不同端之间的特点,在保证各端统一的同时,给开发者流出足够的空间发挥各个端平台的优势能力。而因为Web端的繁荣生态,不仅类小程序形态的单页面应用,TabBar等基础配置能力,我们还期盼才能支持多页面应用,服务端渲染等等能力,提供更多Web应用的业务研制场景。
这种Taro都在生态中逐一实现,有的是postcss插件,例如postcss-pxtransform、postcss-html-transform等等提供了标准化规格、小程序标签转换的能力;有的是babel插件,例如babel-plugin-transform-taroapi会移除不须要的API代码;也有tarojs-plugin-platform-nextjs这样社区写的Taro插件,结合Web生态提供易用的服务端渲染方案……
跨框架组件库
作为一个跨框架的跨端解决方案,Taro也须要一个跨框架可用的标准化组件库。在须要支持多框架,防止冗余工作的同时,我们也不希望自己再度回到刀耕火种的石器时代,于是抱着找寻一个好用的方案为出发点开始找寻,而作为一个系列的技术规范,WebComponents为各大主流的浏览器所支持,如同理所其实的WebComponents成为了那种标准答案。
#0:4:8:c:b:c:8:8:4:4:e:9:2:e:0:8:9:b:d:1:6:6:7:d:4:5:5:7:c:e:5:e#
为了更好的使用WebComponents,又能否兼容我们已有的ReactComponent组件库,我们同时还选择了Stencil帮助我们完成此次转换。其实在通过Stencil提供的WebComponents组件和React、Vue框架结合的过程中,仍然会碰到好多问题,并且通过提供对应的适配器,大部份问题都可以兼容适配,在这儿就不一一展开,具体问题与解决方案在Taro官方博客都可以找到对应文章解析,可自行抵达查阅。
支持编译其它端或平台
在Taro3.x不断发展的过程中,我们也在不断推出小程序端侧插件的同时,为开发者提供了好多小程序之外的选择。
支持编译原生应用
支持编译原生应用,在Taro初期版本中始终都是依靠于ReactNative实现,在Taro3.x中也不例外。通过更贴合ReactNative生态体系的编译工具metro用于打包,虽然这会造成一些问题,例如webpackChain在rn编译时不再支持,不过好在这种问题都有取代的解决方案。
#4:8:9:6:7:f:b:4:c:9:8:a:1:b:e:1:7:5:5:f:7:d:6:2:2:7:e:4:7:7:0:a#
其它方面。诸如路由、组件和API等等在Taro3标准的运行时结合expo生态体系改建后也愈发灵活,可以按项目须要集成依赖;ReactNativeApp接入方案也愈发灵活,不再锁定版本,开发者可以依据须要选择快速开发,或则愈发灵活的配置方案……
支持编译鸿蒙应用
对于Taro来说,我们以插件的方式适配鸿蒙并指摘事,其实为了抚平鸿蒙和其他平台的差别,我们须要针对处理的问题还有不少,比方说支持React&Vue句型,支持标准的组件和API等等,支持句型通过编撰框架的运行时就可以实现,而组件和API则须要通过OH提供的能力来实现。
#e:f:d:b:3:9:d:d:2:1:9:9:8:9:4:7:c:4:3:4:f:f:e:d:1:4:7:6:5:4:a:6#
最终我们就可以看见我们写的代码可通过webpack打包成应用,在后端框架层通过Taro提供的运行时与UI视图交互数据和风波,加上OH提供的基础能力就可以鸿蒙端的适配渲染流程。
Taro和华为以及开放原子基金会仍然以来都有合作,通过加入OpenHarmony并创立CrossPlatformUISig,为鸿蒙跨端能力的支持。而目前在Tarocanary版本中早已支持了鸿蒙,而且稳定的运行了很长一段时间,相信在不久的将来才会与你们碰面。
支持Mcube
对于公司内部的开发者,Taro也在通过和Mcube的合作为开发者提供新的使用场景。督查通过编译时的形式,将Taro项目编译为Mcube支持的格式,并以Mcube标准适配小程序和Web端。
#2:c:2:9:c:d:6:e:b:f:a:2:c:1:6:9:e:2:a:4:3:8:c:3:0:d:7:9:9:4:b:4#
插件发布早期预计支持,通过将使用React编撰的Taro项目编译到Mcube平台,敬请期盼!
未来的发展
随着IOT设备的普及,未来肯定会存在更多的“平台”,面对不同场景会诞生各类跨平台容器技术。为此做好一款跨平台框架,对上统一句型、组件、API标准,对下对接各类跨平台容器,将是Taro框架未来的主要使命。