跨端方案的三大困境

前端桃园

共 4333字,需浏览 9分钟

 ·

2020-08-26 08:40


关注「前端向后」微信公众号,你将收获一系列「用原创」的高质量技术文章,主题包括但不限于前端、Node.js以及服务端技术


写在前面

2018 年,Airbnb 放弃了继续使用 React Native,个中原因主要有两方面:

  • 技术:成熟度、配套设施/类库建设成本、首屏性能硬伤等没能很好地解决

  • 团队组织:工程师要求高、跨技术栈/跨团队调试/测试等也产生了新的问题

实际上,跨端方案遭遇的问题远不止这些,一些时候 Write Once, Run Everywhere 只是美好愿景

一.技术困境

一言以蔽之,触碰到能力边界之前,跨端方案里的一切都是美好的

以 React Native 为例:

Bridge 层通过消息通信将 JavaScript 世界与 Native 世界联系起来

Shadow Tree 用来定义 UI 效果及交互功能,Native Modules 提供 Native 功能(比如蓝牙),二者之间通过 JSON 消息相互通信

P.S.图有些旧,但不影响理解原理,更新的 React Native 架构图见 React Native 架构演进

在这样的技术架构中,写的和实际执行的都是 JavaScript,调用由 Native 提供的视图渲染能力及平台特定能力,Facebook 称之为Scripting native 方案,姑且叫做容器化 Native 跨端方案

将 Native App 改造成标准化的容器,进而允许一套代码跨多端标准容器运行,如 React Native/Weex、Flutter

(摘自移动端跨平台技术之下的变与不变

容器能力在很大程度上决定着开发效率,在容器提供了一致的标准支持范围内,能够愉快地一人搞定多端。然而,一旦触及能力边界,就会面临高成本的多层联合开发(Native 层、JavaScript 引擎层、特定业务领域层、业务层……),人效上并没有优势。另一方面,如何保持多端一致性,也是个极其复杂并且充满技术挑战的问题

此外,相关的配套能力也直接关系到开发效率:

  • 调试:跨技术栈调试一直是难题,问题排查成本越来越高

  • 性能:跨线程、跨页面耗时分析困难、性能工具链缺失

  • 工程链路:由于技术方案的特殊性,需要量身定制许多配套设施(包括但不限于 IDE、调试、性能、CI/CD、监控),才能将各个环节的成本降下来

总的来说,技术上最大的困境在于:

  • 抹不平的多端差异

  • 掀不开的 JavaScript 引擎盖

  • 跟不上的配套能力

多端一致性在技术投入上几乎是无底洞,底层的平台架构差异(UI 渲染方式、事件机制、系统 API)根深蒂固,以各类跨端方案目前的成熟度仅能覆盖极其有限的一部分,留有非常大的空白需要通过扩展容器能力来填补,包括通用的基础能力(如 UI、交互),以及面向业务领域的特定能力(如多媒体、定位)

引入 JavaScript 引擎虽然获得了动态执行代码的能力,但也带来了技术上的不确定性,几乎无法跟踪解决 JavaScript 引擎内部的崩溃或异常行为

通用的基础设施大多无法直接用于跨端场景,边边角角的配套能力都需要花力气建设,而为了满足业务快速生长,总是优先建设核心关键能力,配套支持通常是滞后的,同样影响着效率

Flutter 能带来一些不同吗?

事实上,Flutter(目前看起来)同样面临这些技术困境,技术实现的变化并未彻底改变局面

据 2020 Q1 调查结果,Flutter 开发者认为最重要的 6 个问题是:

  • 调试错误和崩溃

  • 测试确保 App 能够跨多平台运行

  • 选用状态管理方案

  • 理解和处理布局问题(如文本溢出)

  • 根据设计稿创建 UI

  • 排查特定平台问题

同时,认为最困难的 6 个问题是:

  • 排查特定平台问题

  • 内存问题的诊断和修复

  • CPU 使用率问题的诊断和修复

  • 接入现有的 Native API

  • UI 卡顿问题的诊断和修复

  • 开发特定平台的 Flutter 插件

因此,跨端方案的调试、性能之痛仍在 Flutter 延续,多端差异以及配套能力的困境并没有改变

二.团队组织困境

与单端开发模式相比,跨端方案的协作成本更高,体现在:

  • 跨团队

  • 链路长

  • 容器团队压力大

  • 职责边界不清晰

跨端方案下,跨团队协作成为了最主要的协作方式,需求串讲、开发、联调、问题排查等多个环节都需要跨团队沟通/协作,沟通成本不容忽视

长链路意味着技术细节散落在多层,各自只拥有一小部分知识

表层业务逻辑
-----------------------------
特定业务领域框架
-----------------------------
通用前端框架/类库
-----------------------------
JavaScript引擎(扩展)
-----------------------------
Native Module | 特定业务领域能力
-----------------------------
Native通用框架
-----------------------------
Native View
-----------------------------
平台操作系统

由于每个团队都看不到全景,每一个原因不那么显而易见的问题就都要一层层向下排查,甚至涉及特定业务领域能力的部分又分为许多层……

另一方面,如此繁多的层次也造成了复杂度堆积,越往下层复杂度越高,因为不确定的可变输入越多,越难弄明白来龙去脉,排查问题的成本也越高

理想情况下,按漏斗模型逐层过滤,每一层只需要检查自己的输入输出,但滞后的配套能力让表层业务难以识别出问题所在的范围,于是容器团队成为了问题流转的过滤阀,上接纷繁的 JavaScript 业务,下连复杂的特定领域能力,大量的时间耗费在了弄清楚来龙去脉上,容器能力扩展被迫降速,反复排查已知问题……

业务视角下,对业务之下的层次职责划分并不十分清楚,因此很容易找错层/人,产生无效的“重定向”。而容器层同样也不具备全景视图,问题流转轨迹变得相当曲折,沟通成本充斥在各个环节中,制约着开发效率

三.个体困境

对个体而言,面临的最大困难是跨端方案与 Web 标准存在些许差异,并且这些许差异不像 W3C 标准一样能写得清清楚楚:

Weex enables developers to use modern web development skills to build Android, iOS, and Web apps with a single codebase.

也就是说,通用的 Web 经验不完全适用,学习曲线并不十分友好,例如:

  • rem、媒体查询、scale/zoom等适配经验都不一定适用

  • 减少 DOM 操作、合并 JavaScript 文件、开启硬件加速等常规优化措施也不一定能产生明显的性能优化效果

  • (像学习 Web 一样)只了解浏览器之上的标准能力是不够的,想要真正高效地完成业务开发工作,容器原理甚至部分实现细节都要理解

就像有 Native 背景的开发者学习TypeScript一样,初接触无师自通,熟悉的ClassInterface静态类型用起来游刃有余……然而,熟知 TypeScript 的开发者一定知道个中细节存在着多少奇怪的地方

四.跨端的真正意义是什么?

React Native 最初的出发点是:

希望 Native 开发也能像 Web 一样 Move fast

  • 快速迭代(Rapid iteration cycle):Web 一天两版,产品迭代周期更短

  • 快速反馈(Immediate testing feedback):Web 发布立即触达用户,A/B test 等实验结果立等可取,产品演进更快

  • 快速开发(Rapid development velocity):刷新浏览器即可生效,不必等待重新编译 App

黯羽轻扬,公众号:前端向后React Native 从诞生到现在

因此,从需求角度来看,开发效率是次要的,动态化的灵活性、快速迭代助业务先赢才是其跨端的主要意义,或者说追求的是生产效率,而不仅是开发效率,更短的迭代周期,更快速的触达用户都是直接的生产效率进步

然而,在三大困境之下,开发效率实际上也严重影响着生产效率,但还不足以抵消快速迭代、动态发布的重大进步,此消彼长也算是一种平衡,一种可接受的妥协

五.在困境中寻找生门

理想情况下,容器应该是趋于标准化的,提供多端一致、丰富稳定的能力支持,之上的业务栈极少触及容器能力边界,从而使得容器层能够不断优化探索,更好地满足业务发展的需要

另一方面,跨端方案只是将多端不一致性带来的复杂度下沉到了容器层,独立于平台的语言环境(JavaScript 引擎、Dart 虚拟机等)能够保证上层业务逻辑的一致性,但容器层仍然需要在多端各自实现一套,如何保证容器能力的多端一致性,仍然是个大问题,也并不比非跨端方案下容易多少

因此,首先要解决容器能力丰富度的问题,将边界拓宽,从根源上减少问题。转而集中火力到真正的难题上,攻下最有价值的难点部分。同时通过虚拟架构等方式建立全职能的业务支撑团队,降低沟通成本

  • 业务要有自研能力:化解下层资源瓶颈,共同丰富容器能力

  • 专注必须花大力气投入的点:调试能力、标准化、性能分析以及持续跟踪、工程配套设施

  • 要有全职能的业务支撑团队:有能力兜住所有问题,真正提供一揽子解决方案,消除无意义的沟通重定向

其中,业务自研能力先要有标准的扩展方式,要求容器实现上易扩展,业务开发者不需要了解过多细节也能快速进入开发。调试能力在长链路的技术栈下至关重要,问题识别成本越低、准确率越高,效率越高,所能释放出来的资源就越多

从业务开发角度来看,更需要的可能是一层网关,请求过去响应回来,而不是一系列路由表,需要一跳一跳地跟踪。全职能的业务支撑团队组成局域网,让网关之后的流量得以快速流转,高效协作的同时提升业务开发的幸福感

参考资料

  • What is Weex?

  • What are the important & difficult tasks for Flutter devs? — Q1 2020 survey results

联系我      

如果心中仍有疑问,请查看原文并留下评论噢。(特别要紧的问题,可以直接微信联系 ayqywx




推荐阅读




我的公众号能带来什么价值?(文末有送书规则,一定要看)

每个前端工程师都应该了解的图片知识(长文建议收藏)

为什么现在面试总是面试造火箭?

浏览 21
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报