开发者社区> 温柔的养猫人> 正文
阿里云
为了无法计算的价值
打开APP
阿里云APP内打开

跨端的另一种思路 | D2分享+文章

简介: 跨端的另一种思路:逻辑跨端的探索。
+关注继续查看
福利推荐:阿里云、腾讯云、华为云等大品牌云产品全线2折优惠活动来袭,4核8G云服务器899元/3年,新老用户共享优惠,点击这里立即抢购>>>

作者 | 当轩

点击查看视频

大家好,我是来自阿里巴巴集团 ICBU 互动端技术团队的当轩,很荣幸能在第十五届 D2 大会上和大家做一次分享 ,我此次分享的主题是《跨端的另一种思路》。

我在开发端应用的时候,常常怀念 Web 的天然跨平台能力和开发效率。然而在开发 Web 应用时又会苦恼没有端应用那么好的性能和体验。所以就常常在想,有没有一种技术方案,像 Web 一样可以便捷的开发、在所有平台都能运行,同时又有很好的性能和体验。

当时最有名的一句宣传语我相信大家都有印象 -- Write Once, Run EveryWhere ,一次编写,到处运行。

image.png
然而理想很饱满,现实很骨感,不同的跨端方案层出不穷,但是都难以达到我们最终理想中的效果。
image.png
举例来说:

  • Web 作为天然的跨端方案,在研发效率上非常不错,然后性能和体验的优化难度都非常高

  • React-Native 类的方案,性能上要比 Web 好了不少,然后多端一致性和研发效率则较低

  • Flutter 在 App 上的性能和一致性不错,但是在差异比较大的 Web 端性能就非常糟糕了,也无法满足 Web SEO 等诉求

之所以理想和现实存在这种差距,是因为跨端的方案本身在带来收益的同时也意味着在某些方面会增加一定的成本。
image.png
例如我们期望用同一套代码覆盖所有平台的同时,又希望研发的自由度和表达能力不要受到限制,然而这种方案只能使用不同平台表达力的子集。最典型的场景就是小程序的跨端方案,静态转译的小程序跨端方案往往通过限制开发者的表达能力来实现一套代码跨多端。

既然现实中的方案在带来收益的同时同样带来成本,我们就不能一味的追求 Write Once, Run EveryWhere我们的实际场景做出取舍。于是我们就提出另外一种成本更低的思路:逻辑跨端。
image.png
今天以我们 Alibaba.com 的交易场景为例,分为 Android/iOS/M 站/PC 网页几个端。
image.png
几端的 UI 交互并不完全一致,但具有相同的业务逻辑,同时 APP 端对于性能和体验上有更高的要求。我们在效率上最直观的问题在于,一个哪怕非常简单的业务需求的上线,都需要经过多端开发同学的资源协调和相互依赖,消耗在沟通上的成本非常高。

由于 Android/iOS 端对于性能体验的要求高,加上这两端的 UI 和交互基本一致,我们考虑用 Flutter 来作为主要的技术选型。然而在 Native 和 Web 之间,Flutter 并没有成熟的跨端方案,前面我们提到过 Flutter for Web 的性能基本是不可用状态。

通过 JavaScript 容器 + Yoga 做类似于 React-Native 的方案我们也考虑过,然而其带来的建设成本和抽象代价在这种场景下是否合适是存疑的,因为 PC 和 App 的交互完全不同,通过引入 Yoga 类的方案反而会让两端的开发都束手束脚。除此之外,我们也担心 JavaScript 容器的引入在端上带来更高的优化成本。

于是我们就提出了另外一种思路:有没有可能仅针对业务逻辑做跨端共享,这样我们不需要对容器侧进行太大的改造,也不用担心引入新的优化成本,同时也能做到跨端共享代码。从而让前端同学去单独 hold 业务侧的需求成为一个可能的方案。

image.png
那么对于这样一种方案我们会有几个问题:

image.png

  • 采取什么语言

  • 如何分离逻辑和 UI

  • 差异逻辑怎么写

第一个问题是采取什么语言,其实 Dart 就是一个现成的选项,因为 Dart 从一开始就是为 Web 设计的语言,甚至在 Flutter 出现前曾经一度在 Chrome 中有默认 VM 实现。虽然后来放弃了在浏览器中的发展,但是 Dart => JavaScript 仍然是一个成熟的技术。

image.png
同时因为我们的前端同学其实对于 Dart 仍然不是那么的熟悉,另外 Dart 的类型系统在很多时候并不能满足动态类型的诉求,以至于到处都是 dynamic。例如说基础的联合类型:string | number 这样的类型都无法直接支持,所以我们也在探索从 TypeScript 转译到 Dart 的方案。

image.png
通过 TypeScript 提供的能力,我们可以直接把一份 TS 的代码从源码解析到 AST,而后通过遍历 AST 生成对应的 Dart 代码。同时其中通过 getTypeChecker.getTypeAtLocation 等 API 获取到 AST 对应的 TS 类型。然后通过把 TS 类型转换成对应的 Dart 类型。对于不支持的类型降级到 dynamic ,把原有的完整类型信息输出到对应的注释里。

image.png
第二个问题是如何有效的分离逻辑和 UI,其实我们都知道如果只是单纯的把纯函数作为一个独立模块给抽离出来并不困难。然而逻辑中必然不仅仅是单纯的输入得到输出的纯模块,还有很多涉及到组件生命周期、渲染、状态变化等副作用(side effect)。如果我们需要在 Flutter 和 Web 间复用逻辑,我们就需要定义一份类似的接口。

我们可以先看看 Flutter 和 Web(我们这里采用的是 React)究竟存在多大差异,这里是 Flutter 和 React 构建组件的一个简单对比。其实我们知道,在代码组织方式上,Flutter 和 React 是非常相似的,事实上他们背后的的状态更新 => 触发 Diff => 重新渲染的逻辑也基本一致。所以说最理想的情况下,我们能直接用同样的逻辑抽象方式共同来书写逻辑,这样可以避免在不同场景下的接口对接方式和心智负担。

image.png
那么,有什么合适的方案可以用于分离逻辑和 UI 呢。其实对于很多前端同学来说,可能都了解 React 16 推出的 React Hooks,我们可以在这里看到相比 React 15 的 Class API 的一个区别。
image.png
看上去好像除了代码量变小外似乎问题不大?但其实如果我们把这个组件拆成两个函数,就能明显看出差别了。

image.png
没错,React Hooks 最大的作用不在于单纯的少写代码,而是让我们可以以非常低的成本把逻辑从 UI 或者其他逻辑中抽离出来,并且进行再组合。也就是说其实 Hooks 就是这么一个现成的逻辑拆分方案,大家也广泛接受其理念和 API。那么,有没有可能让 Hooks 的逻辑在 Flutter 上也能使用呢?

前面我们提到,Flutter 构建组件的方式以及运行原理都和 React 十分的相似,那么我们其实只要理解了 React 中 Hooks 工作的原理,就能在 Flutter 中再实现一遍。Hooks 可以简单理解为 闭包 + 数组(实际上在 React 中是链表)。以 useState 为例,Hooks 和普通函数最大的差异在于其可以在多次 render 调用中保持状态,实际上就是通过在闭包中保留状态,同时通过每次重新 render 时重置计数,从而依赖执行顺序还原出具体的状态。
image.png
那么到了 Flutter 中,我们就可以实现一个 HooksWidget ,在触发渲染时重置计数,同时把当前组件存储到闭包中,从而让 Hooks 能够根据计数找到对应的状态,并且知道应该去触发那个组件的重渲染。
image.png
最后一点就是对于差异化逻辑的书写,Dart 从 x.x 版本就引入了 condtional import 的能力,让我们可以根据不同的环境(Flutter 和 Web 引入不同的包)。于是我们可以在不同的包中书写接口相同,但底层逻辑不同的类,从而实现差异化逻辑。
image.png
同时在 Web 端,我们可以通过 Dart 官方提供的 js 包,轻松的和浏览器中的 JavaScript 原有能力进行交互。于是我们可以通过这样的一层胶水层,让我们在 Flutter 端的逻辑走我们上面写的 Hooks 能力,而在 Web 端则直接调用 React 的 Hooks。

最后我们能实现的一个效果,就是用同一份逻辑代码来表达 Flutter 和 Web 端的业务逻辑,并且作为我们非常熟悉的 Hooks,引入到 UI 上并且直接绑定。

image.png
image.png
image.png
同时借助 SourceMap,我们在 Devtools 里也能进行调试。
image.png
由于我们在渲染上并不强制差异很大的 Web 和 Flutter 保持统一,所以在性能上可以针对不同平台的特性做出优化,比 Flutter for Web 的性能表现更好。

点击查看视频

这个 DEMO 仅代表这个场景下的一个性能对比,并不代表所有场景下的方案性能都如上所示,事实上针对不同的方案我们也可以再采取不同的优化措施去改进。之所以放这个 DEMO,主要是为了解释这种方案在不经过太大投入的情况下,也能达到理想的性能。

image.png
最后我们再做一个大概的总结:

  • Write Once 是一个理想目标,Write Logic Once 在部分场景下能更好的解决我们的问题。

  • 介绍的方案仅适合重逻辑的场景,对于重 UI 表现场景不适用。我们需要根据场景做出对应的选择取舍。

  • 对于写一份逻辑,无论是 Dart 还是 TS,仍然存在一定的抽象泄露问题,对于一些复杂问题的排查仍依赖开发者的经验


?第十五届 D2 前端技术论坛 PPT 集合已放出,马上获取

image.png
关注「Alibaba F2E」
回复 「PPT」一键获取大会完整PPT

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
Dubbo系列讲解之扩展点实现原理分析【2万字分享】
Apache Dubbo 是一款微服务开发框架,它提供了 RPC通信 与 微服务治理 两大关键能力。这意味着,使用 Dubbo 开发的微服务,将具备相互之间的远程发现与通信能力, 同时利用 Dubbo 提供的丰富服务治理能力,可以实现诸如服务发现、负载均衡、流量调度等服务治理诉求。同时 Dubbo 是高度可扩展的,用户几乎可以在任意功能点去定制自己的实现,以改变框架的默认行为来满足自己的业务需求
88 0
解读如何安全快速建立IT治理环境
云计算经过十多年的发展,从基础的IAAS,大数据,到各种的PaaS有丰富的产品和生态,非常有效地助力了业务增长和技术创新,并提高了业务的效率。最直观的感受是过去需要几天到一个月的资源交付,现在只需要秒级就可以实现。
325 0
解读:如何安全快速建立IT治理环境
2021年11月1日,阿里云"云治理中心"(Cloud Governance Center)产品正式上线,云治理中心是基于企业IT治理的最佳实践,帮助客户快速搭建业务上云的标准Landing Zone(上云登陆区),实现各组织和团队在云上的良好协同、降低风险和提升效率,最大化地发挥云计算所带来的价值。
141 0
C++的另一种错误处理策略
这篇短文是讨论一个大多数程序员都感兴趣的一个话题:错误处理。错误处理是编程的一个“黑暗面”。它既是应用程序的“现实世界”的关键点,也是一个你想隐藏的复杂业务。
85 0
基于逻辑复用的联合跨端思路与实践
跨端新思路助你业务研发事半功倍。
603 0
前端故障演练的探索与实践 | D2分享视频+文章
这些年来,随着前端技术的演进,特别是serverless、跨端、端计算等新技术的引入,前端架构的复杂程度成爆炸式增长。我们尝试通过前端故障演练来提升前端安全生产的水位。
1551 0
文章
问答
来源圈子
更多
+ 订阅
文章排行榜
最热
最新
相关电子书
更多
重新出发:阿里云数据库开源整体策略
立即下载
“移”码平川:移动端高可用性体系
立即下载
“移”码平川-移动端高可用性体系
立即下载


http://www.vxiaotou.com