likes
comments
collection
share

Flutter Web - 让 Web 与 APP UI 一致的另一种可能

作者站长头像
站长
· 阅读数 104

当产品说要把所有移动端(小程序、H5、APP)等 UI 做成一致,又不想投入更多的开发人力。那作为开发,有哪些方案可以用呢?

  1. 整体 Web 化,比如 Canva 就是完全套壳的 APP,用了一套 Web 响应式布局,适配了 All。但缺点也很明显,在 APP 上只能是通过 webview,会频繁的白屏及重绘(看滚动条位置就能看出来,保存不住上几个页面的页面状态),更搞笑的是没做离线化,弱网络打开 APP 会卡在 launch 页,首页都进不去 [手动狗头]。
  2. uni-app 等跨端方案, 好几年前笔者也了解尝试过,这类技术优缺点也很明显,如果是开发简单 App,确实可以一把梭。但凡需要体验更优雅点、功能更复杂点就需要花费几倍力气。
  3. 就是本文要说的 Flutter 了,可以参考 美团外卖在 Flutter Web 上的实践

在 Flutter 3.0 时代后,Flutter 官方算是进一步优化了 build web 的一些问题,减少了包体积,至此,笔者也由于某些原因兴(bei)趣(po)满(wu)满(nai)的开始尝试这大前端极致互卷之路。

思考方案

用 Flutter 来做 Web 最主要的是想复用 APP 的 UI。(其他方面确实赶不上直接用 React / Vue 来开发来的舒适。特别是需要更为重视 SEO 的业务,Flutter 官方对 SEO 都没有任何建议)

先看一下总体架构设计:

Flutter Web  - 让 Web 与 APP UI 一致的另一种可能

这个设计的目的:

  1. 最大程度共用 App 的 Flutter UI 组件。
  2. 尽可能的复用 Web 现有的能力。
  3. 要支撑 Flutter Web 和 Vue Web 页的混用。
  4. 后期可以方便替换成 Rust 完成前后端业务能力大一统。

那其实重点的需要有一个通信层,让 TS / JS 与 Flutter web 可以优雅的通信。

通信层

让 Flutter 开发同学只关心 UI 展示,让前端同学只关心业务实现,尽可能减少沟通及语言学习成本。

那其实通信层上就需要做到双端无感,这很容易想到使用 codegen 的方式(codegen 会单独开一个专栏来聊一聊)。

代码核心上,主要是使用 Flutter 提供的官方一方库 pub.dev/packages/js , 虽然它的版本是 0.6+,没有到 1.0 正式版,但隶属于 dart-lang/sdk 还是可信赖的。

在研究了该库的 example github.com/google/char… (google 组织下,也是很有保证的)后发现了一个官方使用的 codegen 生成 chartjs.dart 类的工具。

 https://github.com/dart-lang/js_facade_gen 》根据 TS 代码生成 dart 抽象调用层。

在各种挠头尝试下,最终确认这库是用不了的 [手动狗头]

用不了的原因也很简单

Flutter Web  - 让 Web 与 APP UI 一致的另一种可能

如上图所示,虽然这库也是在 dart-lang 中,但这库已经3年没更新了,Flutter 这3年大大小小也发了几十个版本,生成的 dart 代码上林林总总各种报错不能用 ...

那是否要重新造轮子?

在研究了它的源码后,发现其实也还是对 TS 的 AST 进行字符串处理(codegen 的本质就是字符串处理 [手动狗头])

那我们就可以改造源码的方式进行本地使用。

例如去支持生成 Flutter 空安全语法:

Flutter Web  - 让 Web 与 APP UI 一致的另一种可能

也做了不少增强性工作,原因是这两点:

  1. 经过验证,虽然 Flutter 调用 JS 没问题,但 JS 对象返回后在 Flutter 上无法被解析,而且在 dart2js / dart2jsdev 上,两者的 JS 对象还是不同的,根本没法直接使用。
  2. 就算把 JS 对象转换成 Map,也不符合我们对 API 的要求,我们希望的是 JS 对象完美体现在 Flutter 上,这样模型层就无需写两遍。

所以我们在 TS 中增加了 @DartObject 注解来做定义,通过内置 json_annotation 直接生成 Flutter Model(当然这需要执行额外的命令,但我们把它封装成一个 vscode extensions 即可很方便使用)。

代码上只要写 TS 代码即可。

/**
 * 首页模版 Model
 */
@DartObject()
export class GDHomeTemplateListModel {
  /**
   * id
   */
  id?: string
  /**
   * 模版名称
   */
  name?: string
  /**
   * 预览图
   */
  preview?: GDHomeTemplateListPreviewModel

  test?: string
}

不能添加视频,所以不太好描述 - -|

举个例子,测试是模版接口调用

下图是 TS 通过 fetchAPI 实现接口调用的测试代码

Flutter Web  - 让 Web 与 APP UI 一致的另一种可能

下图是 Flutter 通过生成的 GDModuleAPI.home.fetchTemplates 方法直接调用 TS 层代码

Flutter Web  - 让 Web 与 APP UI 一致的另一种可能

可以看到 Flutter 正常使用 TS 定义的模型了

再放一张测试结果图:

Flutter Web  - 让 Web 与 APP UI 一致的另一种可能

可以看到,接口请求真实发生且已将模版列表渲染成功。

结论

可以看到过程上做到了极大解耦:

  1. Flutter UI Components 开发
  2. JS API 开发
  3. Flutter 页面拼装 JS API 与 Flutter UI Components

后续

本文主要是记录下调研过程,整体方案已经是可用的,接下来就会进入到方案开发。

至于本文中间生成的过程,后续会单独开篇讲一下笔者开发或用到的各类 codegen 工具。

有任何问题可以评论区问我 ~

转载自:https://juejin.cn/post/7133534750664294430
评论
请登录