阿里跨端技术 - 缘起与未来

avatar
公众号 @fliggyfe

本文来自飞猪用户技术团队圣司,来和大家一起聊聊阿里跨端技术的缘起与未来。

伴随互联网业务形态的多样化,无线流量渠道矩阵逐步成型,更多场景化的流量入口带来了天然细分的人群与触点,流量红利的逐渐式微也让越来越多的业务开始重视用户增长。在小程序、轻应用等开放平台,及腾讯广告、微博、巨量引擎、百度信息流、阿里汇川等商业化引擎能力加持下,全域投放、全域触达、全域营销的思路逐渐成为标配,IoT、OTT、POS 等设备需求也爆发式增长,端侧业务需要在合适的时机,通过合适的端/渠道,出现在合适的人手中形成承接,朴素的业务需求之下,也对面向多端场景的业务研发提出了更高的要求与挑战。


当我们聊跨端时,我们在聊什么?

1990 年,Tim Berners-Lee 在自家的 NeXT 电脑上打开了 Web 技术的大门,并带来了 HTML 和 HTTP,前端技术的种子自此落地,开始生根发芽。随后的三十年风起云涌,从 PC 时代、移动时代到万物互联的 IoT 时代,繁荣的背后越来越多的设备、系统与解决方案纷至沓来。

跨端作为大前端技术的一个分支,重点在于面向不同终端设备,提供高效的业务场景研发解决方案,并保障性能与体验。今天我们提到的跨端,一般包含四类场景:

  1. 跨设备平台,如 PC/Mobile/OTT/IoT。不同的设备平台往往意味着不同的硬件能力、传感器、屏幕尺寸与交互方式,尤其在移动互联网早期,由于研发主力仍在 PC 上,而移动设备作为下一个风口也需要布局,诞生了大量跨平台解决方案,Web 技术下典型的就是响应式布局。

  2. 跨操作系统,如 Android/iOS/HarmonyOS。对于传统前端开发来说,移动端各类操作系统差异已被图形库、渲染引擎等基础设施抹平,并以标准化的形式提供上层 API;但在 RN、Weex 等依赖原生控件渲染的方案下,依然会对跨端差异有明显感知。

  3. 跨 App,如 淘宝/微信/头条。由于移动平台 CS 架构 App 间天然的壁垒,不同 App 间无法再通过超链接的形式完成资源的索引,而是在各个 App 封闭体系内遵循一套自有标准进行资源的索引与定位。而同一业务投放至不同 App 端时,同样需要适配这些不同的路由规则与原生方法。

  4. 跨渲染容器,如 Webview/RN/Weex。前面三类场景催生了不同平台、不同操作系统、不同 App 间解决方案的差异性,无线领域各种 native 化渲染、自绘渲染与魔改 Webview 的方案被设计出来,也大大提高了跨端场景下的迁移成本。

考虑到当下移动领域仍是互联网绝对的统治者,我们先将问题的讨论范围收束到移动技术领域。


跨端技术发展史

2007 年苹果发布初代 iPhone 拉开了移动互联网的序幕,08~14 年迎来了 全球范围年复合 35.1% 的高速增长,伴随终端设备的日趋多样化,PC 之后,Mobile、IoT、OTT 等一系列新设备开始登上历史舞台,而在 Web 标准向移动设备适配的过程中,诞生了种类繁多的跨端解决方案,大体上可以分为几个阶段:

19/20 全球移动开发者跨端解决方案使用率,数据来源 statista

1. H5 wap 阶段jquery/KISSY → Zepto/KISSY-mini。Web 天然跨平台,但由于早期网络环境原因,页面加载速度无法满足业务预期,加之设备传感器标准缺失、内存占用大、GPU 利用率低等问题,在移动设备量爆发伊始,难堪大任的论调一下子被推上风口浪尖,并在 12 年达到顶峰:

"The biggest mistake we made as a company was betting too much on HTML5 as opposed to native. It just wasn't ready." —— Mark Zuckerberg, 2012

彼时标准化组织与浏览器厂商正在酣战,04年 WHATWG 诞生,直至 10年后 HTML5 标准终于发布;相对后来的跨端解决方案,PWA 是 Web 正统,完全的标准化运作实现,但由于受多方阻力,推进缓慢,国际层面 相对有一些声浪,而在国内甚至只能存在于各大厂商的面试题中,近两年受小程序的冲击尤甚。

2. Hybrid 阶段,典型代表是 Cordova/PhoneGap/ionic。从核心功能上看,Hybrid 解决了 web 跨端时代的两大痛点问题,1)性能,依靠容器能力,各类离线化、预装包、prefetch 方案大幅减少页面渲染所需加载资源数量,或提前加载时间,配合一些编码上的优化,在 2/3G 及 4G 时代早期把 Web 的秒开体验拉到了与 Native 一个数量级的水平;2)功能,通过 JSBridge 桥接的方式,规避了 Web 标准制定长流程,及与 Native 原生的割裂带来的底层能力缺失。

Hybrid 本质上是 Web 标准/实现滞后于无线业务发展速度之下的一种权衡与妥协,Web 技术借助 hybrid 的加持在无线时代成功撕开了口子,让前端有了一席之地,也为后来的演进方向作了不少探索与铺垫。但 Hybrid 带来的 Web 标准超集,不同 App 间容器能力、配套设施的差异也导致了 PC 浏览器、移动操作系统之后,前端开发的第三次碎片化。

3. Native 容器化阶段,典型代表是 ReactNative/Weex。通过 JSC(V8)链接 Web 生态与原生控件,结合 React/Vue 等框架,尝试在研发效率与性能体验间寻找更佳的平衡点,也为前端与无线的协同模式开辟了一条新的道路,各类领域解决方案(受限 DSL + 魔改 web 标准 + Native 渲染能力)开始涌现。Native 容器化拉开了大前端融合渲染方案的序幕,传统前端/客户端的职能边界开始变得模糊

"It's worth noting that we're not chasing 'write once, run anywhere.' Different platforms have different looks, feels, and capabilities, and as such, we should still be developing discrete apps for each platform, but the same set of engineers should be able to build applications for whatever platform they choose, without needing to learn a fundamentally different set of technologies for each. We call this approach 'learn once, write anywhere.'" —— Tom Occhino, 2015

相比 RN “Learn Once, Write Anywhere”,Weex 的 “Write Once, Run Everywhere” 更理想化也更贴近前端的思想,但从两者的发展脉络上看,两个弊端无法回避:1)Native 容器化本质上没有消灭一致性问题,而是把问题转移到了容器层解决,瓶颈依然存在;2)伴随业务需求的复杂化,每个新特性的支持都会带来容器复杂度的升高,进而影响到性能/体验/稳定性,更多的分层也带来了问题排查难度的提升;当这两个问题达到某个临界点,Native 容器化方案表现甚至会弱于传统 Web,“屠龙少年最终变成了恶龙”。

数据来源 google trends

4. 同层渲染阶段,典型代表是 WVC/BlendUI/CoverView。是一种介于 Hybrid 与 Native 容器化之间的中间态跨端增强渲染方案。早期通过在 Webview 上直接绘制 Native 组件并跟随滑动的方式,实现客户端内视频等富交互组件原生化,增强体验;之后借力 Android U4 与 iOS WKWebView 提供的同层渲染技术,可以使 Native 组件像普通 DOM 元素一样使用,滑动跟随、层级控制/遮盖、事件透传、生命周期打通,跨端场景下能够更好贴合所处渲染环境的自有方案,渐进增强。严格来说相较其他几个阶段,同层渲染技术并没有达到同样体量的影响力,但它所代表的思想对于当下与未来诸多方向的融合具有重要意义,因此在此也作简要记录。

5. 小程序/快应用阶段,典型代表是 Taro/uni-app/mpvue小程序的核心是商业模式,而非技术实现,它不为解决跨端问题而生,在此将它列为一个阶段,是因为双线程模型及自建 DSL 的引入带来了新的跨端问题。

从技术上说,小程序跨端方案大体分为编译时与运行时两类,前者主要通过静态编译的方式将基于 React/Vue 等框架的业务代码转换为小程序原生 DSL,如 Rax 编译时、Taro 2.x,优势在于性能相对较好,但受编译限制需要使用非标的受限语法,研发体验比较差;后者则通过运行时框架对接小程序 setData 的方式来实现跨端,优缺点与编译时正好相反;按具体实现不同运行时方案可再细分为两类,1)利用 React 框架特性,通过 react-reconciler 在 VDOM 层直接对接对接小程序渲染,如 Remix;2)通过模拟 DOM/BOM API 的方式,在运行时框架层维护 VDOM 并对接小程序渲染,如 KBone、Rax 运行时、Taro 3.x,这个方案理论上可以对接 react、vue、Angular 等任何上层框架。

小程序在帮助头部 App 建立起封闭流量体系的同时,也让国人第一次在跨端技术领域 “走在了世界前列”(手动狗头保命),各类开发框架、配套工程体系及魔改 DSL 在神秘的东方国度创造出了 “小程序宇宙”(Rax、Remix、wepy、mpvue、uni-app、Megalo、mpx、Chameleon、Taro,相信你还能说出不少),魔幻而现实。

6. 自绘渲染阶段,如 Flutter/Qt。自绘本身不是个新词,在此把自绘和 H5、原生渲染并列也不够严谨,这里的 “自绘” 更强调不使用系统原生控件或 Webview 的渲染管线,而是依赖 Skia、Cairo 等跨平台图形库,自底向上自建渲染引擎、研发框架及基础配套的方式,解决跨端一致性问题。自绘渲染能作为一个独立阶段,很大程度上源于 Flutter 这个跨端研发新贵的崛起,双端一致、HMR、状态驱动,以及 Dart 带来的异步编程体验等特性迅速点燃了客户端研发领域。

然而自绘也并非万能,与 iOS/Android 原生设计语言分离,对于使用原生控件、遵循平台交互视觉标准的存量 App 接入不够友好,未来系统层面规范升级也需要投入更高成本适配;2020Q1 调研拿到的 反馈数据 显示,调试、Debug、崩溃、内存问题等 RN/Weex 曾经历的阵痛 Flutter 也一个不缺;从 社区层面 看,生态建设繁荣,但达到成熟的程度尚需时日。

对于前端来说,Dart Framework 本身上手成本并不高,甚至相较客户端,前端能更快适应它的语法与设计模式,但 JS 的背后是标准与生态,切换到 Dart 远不只是学习一门新语言那么简单,而是庞杂生态秩序的重建。从数据上看,Flutter 虽然增长迅猛,但对 RN 等偏前端驱动的解决方案目前 有很多讨论,但没有造成太多冲击,而更多是替换掉了 Cordova/Xamarin 等传统解决方案,未来其在大前端领域的定位仍需摸索,与前端的关联关系也需我们更积极地探索。

其他涉及跨端的方案还有很多,如 Xamarin/SwiftUI/Kotlin/Qt/Unity/Electron 等,考虑到这些方案或不面向前端领域、或在国内缺少广泛受众和讨论、或不以无线领域为重点、或已进入衰退期,在此略过不表。

纵观跨端技术发展史,想象一根线段,左边是 H5,代表研发效率与动态性;右边是 Native,代表高性能与体验,这几年技术演进的过程,就是通过不断在两个端点间找寻更好平衡的过程,方案无绝对的好坏,而更多是看业务场景诉求,没有银弹,未来这些方案也将在很长一段时间内和平相处,共同构建起大前端的边界与生态。


阿里跨端方向现状与思考

阿里跨端技术发展脉络

伴随前面提到的各类跨端技术方案发展,可选解决方案愈发涌现的同时,也带了越来越重的历史包袱;在阿里 App 矩阵成型,越来越多二方联动、三方通投场景出现的同时,多研发框架和多渲染引擎带来的问题日益凸显,业务承接效率与跨端性能体验也面临前所未有的挑战。

财年初我们对经济体超过 25 个 BU/部门端技术现状进行了 1v1 沟通交流,从采样结果上看,91.7% 的团队业务涉及多 App 端(手淘、支付宝、飞猪、高德、钉钉...)投放场景,其中涉及大量工程能力不统一、跨端 API 不统一、跨端埋点/账号不统一等问题;在此基础上,超过 95.8% 的部门存在多渲染容器适配的场景,在一/二/三方容器选型不统一情况下需要同时兼容 Web/Hybrid/Weex/小程序等渲染方案,也暴露出多套 DSL 重复开发成本、性能体验不统一及搭建能力缺失等问题。

传统角度看跨端技术,聚焦的点无非性能体验、一致性、研发效率,这些问题过去存在,当下存在,未来很长一段时间也会持续存在,从跨端技术背景和阿里经济体技术现状出发,掰开揉碎看这些问题背后的本质:

  1. 难以单纯依靠硬件升级解决性能体验问题。摩尔定律下硬件技术的突破没有带来预想中的性能体验问题默认解决,业务研发中,依然需要投入不小的精力进行针对性优化,究其原因,本质上是端技术架构与业务功能复杂度的提升对冲掉了硬件升级带来的性能红利。并且伴随移动互联网流量见顶,业务进入存量博弈阶段,复杂度会进一步提高,纯粹的 Web 解决方案天花板依然会长期存在,因此结合 PHA/SPA/SSR/Snapshot/Prerender 等方向,各业务独立定制容器侧渲染方案依然无可避免;

  2. 无限的业务场景与有限的能力边界间难以平衡。在上面问题基础上,面向自身业务场景自建解决方案几乎是必选项,类似手淘的 Weex、支付宝的小程序、高德的 AJX。自建解决方案往往都有一个美丽的开始,轻装上阵面向一个特定场景高效解决,之后伴随支持业务范围的拓展,一方面受工期压力部分标准实现开始变形,出现各种阉割与超集,二方通投成本陡然升高;另一方面,每个新特性的支持,都会与老特性间产生笛卡尔积式的复杂度上升,带来各种性能/稳定性问题,最终一旦越过某个临界点,性能体验红利被蚕食殆尽。Airbnb 的 RN、Weex 都有过相似的经历。因此自建解决方案需要回答好两个问题,1)缺乏标准,如何做好存量业务迁移与多投业务支持;2)面对无限的业务场景,如何定义有限的技术边界并控制好复杂度。

  3. 前端与无线不匹配的技术演进节奏,重构沦为常态。从 H5 与 Native 技术演进上看,前端技术更多聚焦在编程思想突破上(MVC/MVVM/reactive/one-way data flow),而容器技术更多聚焦在性能与体验上(Bridge/Native 化容器/自绘渲染引擎)。两者背后的主导力量不同,发展很难说是同步的,而新的编程思想与新的渲染容器技术往往都会带来新的解决方案、研发配套,直接导致 1)业务代码需要跟随两条主线频繁重构,“天下苦秦久矣”,以飞猪为例,15年至今,经历了 KISSY(PC) → KISSY-mini(无线化) → React(框架升级) → ReactNative(Native 渲染) → Weex(MVVM) → Rax 0.6(框架统一)→ Rax 1.0(Web render)→ Rax 1.0 运行时(小程序),平均不到一年一次重构;2)演进与迭代过程中进一步加深跨端技术方案的碎片化,包括端内框架/容器的碎片化(稳定性风险),与跨端投放不同 App 异构解决方案的碎片化(高兼容成本)。

  4. 技术方案供需方不统一,地位极不对等。跨端投放本质上是业务的多渠道分发、获客与服务,有强诉求的往往是没有自有流量入口或自有流量不足的小公司/业务团队,体量小,很难对渠道 App 技术选型有话语权(例如没法要求微信小程序支持用 Flutter Dart 开发),解决办法无非:1)一端一策,以飞猪为例需要适配手淘、支付宝、钉钉、高德、UC、微信等 App 外加 iOS/Android/Web,成本极高,有限研发资源限制下颇有“何不食肉糜”的意味;2)面向多端解决方案构建上层框架,取能力交集,在能力、效率与性能体验间找平衡点,大多数情况下这是更优的选择,但也存在不小的兼容适配成本。

业务最本质的诉求,是在一套研发流程下,一份业务代码(哪怕对代码规范存在一定限制)可以跨端适配,同时在新的 App 或渲染容器方案之上,能够实现业务代码的无缝平迁,“我重构是因为我真的想重构,而不是因为底层渲染方案要迭代更新”。跨端技术方案的不收敛,无形间在业务研发与容器间形成了一张复杂度 O(MxN) 的网,连线背后是指数级增加的沟通、适配成本。最优方案是什么?理想情况下所有可投渠道提供统一的渲染容器解决方案(有 Web 那味儿了),复杂度降低到 O(Mx1);但不同 App 渠道背后代表的不同业务特点往往难以通过一套解决方案弥合,一旦各端开始特异化发展,跨端的高门槛问题便会卷土重来。

回到问题本身,既然前端研发框架、Native 渲染容器及研发配套设施三者承载了不同的使命,又各自有不同的演进节奏,那能否通过制定一套标准体系,实现三者之间的解耦,“上帝的归上帝、凯撒的归凯撒”?

前端的本源即是标准。在 W3C、WHATWG、TC39 等标准化组织的运作下,浏览器碎片化、系统碎片化问题逐步收敛,带来了 Web 生态的繁荣。相同的思路,通过标准化运作机制,对上提供一套研发标准指导,对下推动各渲染容器方案兼容实现,类比浏览器,保障一份业务代码在不同渲染容器间的无缝适配与平迁。子集标准的制定应尽量遵从 Web 标准,这样做有诸多好处:

  1. 复用最佳实践(经验/生态/人才),同时控制实现成本;
  2. 更低的复杂度,通过精简布局等方式带来的高性能;
  3. 与 Web 标准的兼容,切换到 Web 渲染进行兜底或外投的可能性。

同时,需要遵循几个原则:

  1. 明确是以 Web 标准子集的形式输出,保障符合标准的业务能够使用 Web 容器作兜底;
  2. 从研发体验与性能开销两个维度出发,形成分级规范,并利用文档、CanIUse、IDE Plugin 等形式显式标注对应特性所属分级及容器支持范围;
  3. 允许标准外的扩展能力支持,类似浏览器私有属性。

通过标准完成三者的解耦后,我们能够有机会以较低的成本推动上层研发框架的统一,并在不同环境下无缝替换到不同的容器上,配套的工程化、搭建、Hybrid API、性能优化方案及基础组件也能够基于标准各自独立发展,并由各个独立业务自行搭配形成自己的解决方案。

再往前走一步,Flutter 无疑是当下最炙手可热的大前端技术,站在前端的角度,我们如何看待它未来的定位?相信很多同学也都有类似的疑问。本质上 Flutter 让大前端看到了统一底层渲染能力的可能性,试想如果客户端开发(Dart)、动态模板开发(DX)、前端开发(Web)、互动玩法开发(Canvas)底层能够复用一套类似 Flutter Engine 的底座,首先能够复用底层优化(如面向低性能硬件),其次面向未来 IoT 等新场景(如 FuchsiaOS),解决方案与生态配套也可以以更低成本复用统一。

Flutter for Web 当前的两套解决方案

对于 Flutter 与 Web 标准的对接,Google 官方本身已经在 2019 年 I/O 大会 公布了代号 HummingBird(后改为 Flutter for Web)的项目,让 Flutter 可以运行在 Web 环境上,实现方案可以 参考这里,主要需要解决两个问题,其一是在 Web 环境中运行 dart,这部分 dart 从设计之初就有考虑,基于 dart2js 很容易实现;另一个是 Flutter Engine 的渲染能力在 Web 端的对齐,目前主要是两套方案:

  1. HTML Mode,优先通过 HTML 与 CSS 来对齐 UI 能力,实现标准标签,再通过 Canvas 与 SVG 实现自定义标签,最终完成 DOM 树的构建。这个方案也是 FFW 最初的方案,存在两个方面问题,1)生成的 DOM 树结构复杂,层级较深,且跨平台一致性难以保证;2)Canvas 部分事件、交互能力需要自行支持,像素化性能也存在问题。
  2. WebGL Mode,将 Skia 编译为 WebAssembly 库 CanvasKit ,并通过 WebGL 完成页面渲染。这也是目前 Google 在重点推进的方案,相较 HTML Mode 代码体积会更大,但能够得到更好的一致性及视觉还原度。

FFW 是从 Flutter 技术看向 Web 的一个视角,当前阶段还在实验室到工程体系的路上,性能、一致性方面尚存较多问题;对业务开发来说,中短期内更多是作为 Flutter 渲染异常时的兜底方案存在。让我们换个视角,从 Web 来看 Flutter,它的创始团队具备大量 Web 内核研发背景,这也保障了 Engine 层有更优雅的渲染管线;在上文基于分级标准隔离前端业务代码、研发框架与客户端渲染容器的基础上,试想如果能够在已接入 Flutter 的场景下借助底层能力提升性能,岂不妙哉?我们把这个方向定义为 Web on Flutter,一个新的渲染容器建设方向。

Web 与 Flutter 对接的几种方案,引自「如何实现 Flutter 和 Web 生态的对接」 @门柳

WOF 从阿里外部的 MXFlutter,到内部的 Unicorn、Kraken、WFlutter,已有诸多尝试,目前最佳实践尚在探索之中,涉及到的技术方案在此先略过不表,感兴趣的同学也欢迎与我们沟通探讨。需要注意的是,目前 Flutter 本身仍在快速的更新迭代中,实施策略需要考虑 Flutter 未来演进的大方向,借力基础设施,避免过早出现技术体系的分化。


总结与展望

从 IE6,到 Chrome;从自动屏蔽图片的 wap 浏览器,到即用即走的小程序;从应用开发/UED 兼职写页面,到前端成为独立岗位,跨端这个话题几乎贯穿了前端技术发展的全过程。能够有机会投身到这滚滚洪流之中,成为推动技术历史发展的一员,我们无疑是幸运的;伴随跨端技术的演进与迭代,我们的角色也不断在 “标准的捍卫者” 与 “标准的破坏者” 间反复切换。

Serverless、AI、可视化、IoT、AR/VR/MR、端智能、微前端、D2C/P2C/S2C……未来的画卷波澜壮阔,也正藉由一双双手下一行行代码变为现实。梦想可贵,天生无畏,让我们团结起来,Make Web Great Again!