前端开发如何正确地跨端

北京中科程少为 https://m-mip.39.net/baidianfeng/mipso_8505703.html

阿里妹导读:面对多种多样的跨端诉求,有哪些跨端方案?跨端的本质是什么?作为业务技术开发者,应该怎么做?本文分享阿里巴巴ICBU技术部在跨端开发上的一些思考,介绍了当前主流的跨端方案,以及跨端开发的经验心得。

文末福利:云开发是一种什么样的体验?

跨端Writeonce,runeverywhere。我们都听说过这句经典的宣传用语,后来我们都知道,没有什么东西是可以真正runeverywhere的,充其量也只能做到debugeverywhere。而当我们谈论一次编写多端运行时,显然不可能真的指跨一切所有端,大多数情况下你不会需要在电脑和手环上同步开发一个功能。

跨PC和无线端。

跨多Native平台:例如跨Android和iOS,甚至跨Windows。

跨投放APP:随着超级APP越来越多,很多业务需要在多个APP中投放同一个页面。

跨Web和APP:Web在很多情况下仍然是不可避免的,我们的页面可能需要分享、SEO或者投放到M站上等等,这时候就需要同时能在Web和APP内运行。

跨Web、多小程序、QuickApp等:其实本来类似跨APP,但是奈何小程序本身是各家控制的封闭生态,故而有了开发一次适配到多种封闭生态的诉求。

其他端的跨端诉求:例如跨POS机,手表等。

与我们多种多样的跨端诉求相对应的,是百花齐放的跨端方案。百花齐放的跨端方案以Web为基础的H5Hybrid方案这类方案最为直接,简单来说就是用网页来跨端。由于我们绝大多数端上(甚至包括封闭的小程序生态)都支持Webview,所以只要开发网页然后投放到多个端即可,在桌面端对应的方案就是Electron。为什么不直接全用Web?从开发成本低、标准统一、生态繁荣上来说,H5Hybrid方案基本是不二之选。然而这种方案难以避免在性能和体验上存在差距。Web的生态繁荣来自于其良好的历史兼容性,也意味着沉重的历史包袱。

W3C标准作为开放技术标准,历史包袱多,逻辑复杂。

Web标准在设计上不是DesignforPerformance的,导致很多地方难以进一步改善,例如JS执行和Layout、渲染互斥无法并行,导致过长的JS执行任务会执行正常的渲染导致卡顿。

Web的标准化在推进上也比较慢,新的能力可能要比较长的时间才能使用。

React-Native/Weex类方案在移动平台上尤其是早期WebView的性能体验非常糟糕,前面我们也提到这种差距主要来自于Web生态本身沉重的历史负担。而React-Native/Weex这类方案通过尽可能的取长补短,通过结合Web的生态和Native的组件,让JS执行代码后用Native的组件进行渲染。由于抛弃了Web的历史包袱,这类方案可以做一些大刀阔斧的改动。例如RN就如下图中,把JS执行、布局(Yoga)和渲染(Native组件)放在三个进程分开执行,避免了JS执行复杂任务时界面卡顿。通过抛弃CSS中的大量标准,只支持部分flex布局能力来减少布局和渲染的复杂度。这种方案同样存在一些缺陷:

iOS/Android双端本身不一致的组件和布局机制,让双端一致性难以得到保障。

依赖于Native机制也让一些CSS属性实现起来比较困难,例如老大难的z-index问题。

而最麻烦的一点在于,这套方案意味着非常高的维护支持成本。

借用了Web的生态但并不完全是Web生态,很多地方不一致,最常见的吐槽就是惯用的CSS布局方式无法使用。

相比于浏览器新增一个传感器API都要配套完善的devtool,这类方案大部分情况下的开发体验保障可以说是刀耕火种(下图为Chrome的方向传感器API的devtool)。

在WebView性能差距逐渐缩小的今天,维护这一套复杂方案的ROI是否值得,需要根据我们场景的具体诉求考量。FlutterFlutter要解决的问题和上面的方案不同,完全不打算继续在Web生态上借力,从设计之初也并没有把Web生态考虑进去。相比于RN依赖NativeView渲染,Flutter则是自绘的组件,直接通过Skia绘制到屏幕上。由于可以完全发挥GPU的能力,也不需要去Native绕一圈。Flutter理论上能做到更好的性能和两端一致性,这一意味着理论上未来可能基于Flutter的JS动态化方案能够在样式上支持的比WEEX更好。从前端的视角看仍然更像是一个Native开发方案而非跨端方案(虽然其实是跨Android/iOS的)。目前最主要的问题是FlutterforWeb从技术原理上来说离生产可用可能还非常遥远。除此之外动态化能力的确实也会让部分场景不适用。研发框架for小程序小程序是被创造出来的问题,各家小程序出于商业上的考量主动在Web生态的基础上构造了相对封闭的生态。导致和Web生态格格不入。然而有多端小程序投放,或者同时投放小程序和Web端的场景难以接受使用。由于小程序的端封闭且不受控,要解决小程序的跨端问题往往只能从研发框架层面出发。编译时方案比较知名的编译时方案是Taro,大致的原理可以解释为将JSX编译到小程序的WXML/WXSS/JS上,而这类框架的实现原理其实并非真的是一个React或者类React框架,而是把看起来像是JSX的模板通过静态编译的方式翻译成小程序自身的模板。这样做的限制非常明显,那就是JSX是JavaScript的拓展语言(ReactBlog写的是isasyntaxextensiontoJavaScript),而小程序所采用的WXML却是一个表达能力非常受限的模板语言,我们不可能完成从一个通用编程语言到模板语言的编译。而静态编译类框架为了做到这一点,采取的方式就是限制开发者的写法,这也是为什么taro对JSX的写法做出了诸多限制。这一点直接导致了无穷尽的维护成本和严重受损的开发体验,而后taro/next也转向了运行时方案+静态编译优化的结合。运行时方案不谦虚的说,针对小程序的运行时方案应该是最早我在写下remax第一个issue时[1]提出的。通过ReactReconciler(类似于RaxDriver)我们可以让运行在小程序容器中的React不去直接操作DOM,而是把操作的数据通过setData传递给小程序的View层映射到最后的界面上。虽然Remax、Rax运行时、TaroNext等几种方案不尽相同,但是思路大同小异,就是利用小程序模板一定程度上的动态化能力+类React框架的VirtualDOM来进行渲染。当然这种做法相对于小程序原生的渲染方式存在一定的性能损耗。remax的支付宝端性能测试在部分场景下,这种损耗是值得的。这些运行时框架也都在陆续通过允许关掉编译产生的模板中的不用的属性、部分静态编译、虚拟列表等方式来改进性能。当然了,最后内嵌Webview仍然是一个方案。作为业务技术团队,我们该做什么上面介绍的都是针对某些具体的场景的一些解决方案,然而对于业务技术团队来说,跨端的本质是提效。针对新的变化提出新的方案是一方面,更重要的如何让这种提效真的长治久安,让我们的提效不会变成从一个新方案跳到另外一个新方案。让我们重新看上面这张图,可以确定的是,跨端的诉求和与之对应的方案仍然会处于频繁的变化中,也不会出现一个解决所有跨端问题的方案。而其中相对不变的部分是值得我们为了长治久安必须要投入的。WebViewH5HybridWebView可能是众多容器中最为特殊的一个,虽然很难满足部分场景对于性能和体验的极致要求,但是会是最稳定、长期存在且得到支持的方案。从开发效率和未来长期的维护演变来看,在能够满足性能体验要求的前提下,Web方案仍然是最优先应该考虑的。同时,在APP的WebView容器上我们能做更多的工作,例如通过容器来提供一些端内的能力,结合Native能力实现的并行数据加载,页面保活等等。基础建设无论采用何种跨端方案,在哪个容器中,性能、稳定性、效能都是绕不开的三驾马车。性能对于不同的方案往往存在不同的性能方案,上面我们也提到在小程序的运行时方案中就会有减少编译模板产出的字段这样的优化。然而,其实除了这种特定方案的优化外,大部分优化手段是大同小异的:离线缓存、数据预取、快照、SSR、NSR等等方案。对于不同的端和容器,对于性能问题的度量和发现也应该是一致的,我们需要对页面在不同端的性能究竟如何有明确的感知和横向对比。性能的端侧建设(端能力、具体到某一个端的性能测算方案、性能打点等)可能需要根据不同的端、不同的跨端方案而不同。但性能的基础建设(首屏标准、数据分析、基础优化能力)在跨端中应该是相对稳定的。在端侧能力方面,ICBU早期在WEEX性能优化时引入了并行加载的能力,通过wh_prefetch协议来使用容器的并行加载能力。而后在新的容器(WebView、浏览器)中,虽然底层能力存在差异,但仍然识别相同的协议。在数据采集和分析方面,我们通过统一跨端基础库,让不同端不同技术方案可以在同样的标准下分析、度量和对比。稳定性建设在无线端我们常常把性能稳定性并称为“高可用”,稳定性主要涵盖的范围包括灰度能力、业务监控、报警、错误监控、白屏检测等等。这些能力相比起来对具体端和跨端方案的依赖更少,除了在端侧的数据采集逻辑稍有区别外其他建设部分相对也是比较稳定的。集团针对这些场景也存在一些跨端可用的方案、例如iTrace等。工程基建对于不同场景的跨端,虽然在方案上存在一定的差异,但是我们的工程基建是可以保持统一的。

容器层提供统一的API和文档能力

统一的研发流程

研发工具提供统一的抓包、debug能力等

一致的工具库等等

这样,当有新的容器或者方案出现时,我们只需要按照相应的能力进行对齐,就能让我们上层的业务代码和开发体验维持相对稳定的状态。业务逻辑跨端相对来说,我们会发现在多种跨端方案的演化中,如何渲染、如何布局等UI层面的变化要远远大于业务逻辑层面。甚至是小程序和Flutter,其大致的开发范式都没有发生太大的改变。例如Flutter开发范式和React非常相似,同样是声明式UI,同样存在VirtualDOM。考虑到SEO和性能等问题和Flutter本身基于Skia的渲染模式,FlutterforWeb在相当长一段时间内可能都不会是一个生产环境可用的方案。而在统一了业务逻辑代码的组织方式后,我们可以通过HooksforALL的方案让Flutter和Web端可以共享一份基于Hooks的业务逻辑代码。有时候你不需要真的runeverywhere,能够提高效能、保持一致就已经达到目的了。视图层目前来看视图层的跨端仍然充满了变数,在我们的业务逻辑层跨端做的足够原子化后,也许我们部分交互逻辑不是特别重的视图层能够通过DX+绑定原子化逻辑+数据参数的方式覆盖更多的跨端场景。从而同时满足性能、效能方面的诉求。然而对于通用场景的视图跨端,仍然没有银弹。总结总体来说,跨端处于且将长期处于多方案并存且不断变化的状态。除了针对新的变化新的场景选择或创造合适的方案,我们更要做好这种动态变化中长治久安的部分:

H5Hybrid。

性能、稳定性、效能三驾马车的统一性和延续性。

在不强求writeonce的场景下,考虑比UI跨端更简单的业务逻辑跨端。

招聘

阿里巴巴国际站(ICBU,Alibaba.


转载请注明:http://www.guyukameng.com/aspnet/aspnet/2021-03-24/12820.html

  • 上一篇文章:
  •   
  • 下一篇文章: 没有了