跨端的另一种思路 | D2分享+文章-阿里云开发者社区

开发者社区> 温柔的养猫人> 正文

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

简介: 跨端的另一种思路:逻辑跨端的探索。
+关注继续查看

作者 | 当轩

点击查看视频

大家好,我是来自阿里巴巴集团 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

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

相关文章
阿里云服务器怎么设置密码?怎么停机?怎么重启服务器?
如果在创建实例时没有设置密码,或者密码丢失,您可以在控制台上重新设置实例的登录密码。本文仅描述如何在 ECS 管理控制台上修改实例登录密码。
10027 0
阿里云服务器ECS远程登录用户名密码查询方法
阿里云服务器ECS远程连接登录输入用户名和密码,阿里云没有默认密码,如果购买时没设置需要先重置实例密码,Windows用户名是administrator,Linux账号是root,阿小云来详细说下阿里云服务器远程登录连接用户名和密码查询方法
11589 0
阿里云服务器如何登录?阿里云服务器的三种登录方法
购买阿里云ECS云服务器后如何登录?场景不同,阿里云优惠总结大概有三种登录方式: 登录到ECS云服务器控制台 在ECS云服务器控制台用户可以更改密码、更换系.
13841 0
windows server 2008阿里云ECS服务器安全设置
最近我们Sinesafe安全公司在为客户使用阿里云ecs服务器做安全的过程中,发现服务器基础安全性都没有做。为了为站长们提供更加有效的安全基础解决方案,我们Sinesafe将对阿里云服务器win2008 系统进行基础安全部署实战过程! 比较重要的几部分 1.
9154 0
阿里云ECS云服务器初始化设置教程方法
阿里云ECS云服务器初始化是指将云服务器系统恢复到最初状态的过程,阿里云的服务器初始化是通过更换系统盘来实现的,是免费的,阿里云百科网分享服务器初始化教程: 服务器初始化教程方法 本文的服务器初始化是指将ECS云服务器系统恢复到最初状态,服务器中的数据也会被清空,所以初始化之前一定要先备份好。
7347 0
阿里云服务器如何登录?阿里云服务器的三种登录方法
购买阿里云ECS云服务器后如何登录?场景不同,云吞铺子总结大概有三种登录方式: 登录到ECS云服务器控制台 在ECS云服务器控制台用户可以更改密码、更换系统盘、创建快照、配置安全组等操作如何登录ECS云服务器控制台? 1、先登录到阿里云ECS服务器控制台 2、点击顶部的“控制台” 3、通过左侧栏,切换到“云服务器ECS”即可,如下图所示 通过ECS控制台的远程连接来登录到云服务器 阿里云ECS云服务器自带远程连接功能,使用该功能可以登录到云服务器,简单且方便,如下图:点击“远程连接”,第一次连接会自动生成6位数字密码,输入密码即可登录到云服务器上。
22366 0
阿里云服务器ECS登录用户名是什么?系统不同默认账号也不同
阿里云服务器Windows系统默认用户名administrator,Linux镜像服务器用户名root
4492 0
1172
文章
2
问答
来源圈子
更多
+ 订阅
文章排行榜
最热
最新
相关电子书
更多
《2021云上架构与运维峰会演讲合集》
立即下载
《零基础CSS入门教程》
立即下载
《零基础HTML入门教程》
立即下载