Flutter动态化调研实践

4,977 阅读10分钟

一,前言

1,什么是动态化?

目前移动端应用的版本更新, 最常见的方式是定期发版,无论是安卓还是iOS,都需要提交新的安装包到应用市场进行审核。审核通过后,用户在应用市场进行App的下载更新。

而动态化, 就是不依赖更新程序安装包, 就能动态实时更新页面的技术。

2,动态化的必要性

为什么需要动态化技术呢? 因为上述定期发版更新应用的方式存在一些问题,比如:

  1. 审核周期长, 且可能审核不通过。 周期长导致发版本不够灵活, 紧急的业务需求不能及时上线。
  2. 线上出现急需修复的bug时,需要较长修复周期,影响用户体验。
  3. 安装包过大, 动辄几十兆几百兆的应用升级可能会让用户比较抗拒。
  4. 即使上线了,也无法达到全部用户升级, 服务端存在兼容多版本App的问题。

面对这些问题,如果能实现app增量、无感知更新,实现功能同步。无论是对公司还是用户都是非常重要的需求,能实现app动态化更新就显得非常重要,能很好的解决以上问题:

  1. 随时实现功能升级,不存在应用市场长时间审核和拒绝上线问题,达到业务需求快速上线的目的。
  2. 线上bug可以实时修复,提高用户体验。
  3. 可以减小发版功能包体积,只需要替换新增功能即可。
  4. 功能保持一致,类似网页一样,发版后用户同步更新,不存在旧版本兼容问题。

接下来,我们就来分析一下,目前业内主要的Flutter动态化更新方式。

二,动态化方案调研

在Flutter实践层面,简单来说分为三个流派:

  • 方案一:JavaScript是最好的语言(🤣碰瓷PHP) 主要思路:利用Flutter做渲染,开发使用js,逻辑层通过v8/jscore解释运行。代表框架是腾讯的MXFlutter。这个框架是开源的,大写的👍。

  • 方案三:布局,逻辑,一把梭

    主要思路:与方案一最主要的区别是,逻辑层也是使用dart,增加了一层语法解析和运行时。有一个代表,美团的MTFlutter,然而没有开源动向,无从考察更多。

  • 方案二:DSL + JS

    主要思路:基于模板实现动态化,主要布局层采用Dart转DSL的方式,逻辑层使用JS。代表框架是58同城开源的Fair

MXFlutter

项目简介

MXFlutter 是一套基于 JavaScript 的 Flutter 框架,可以用极其类似 Dart 的开发方式,通过编写 JavaScript 代码,来开发 Flutter 应用,或者使用 mxjsbuilder 编译器,把现有Flutter 工程编译为JS,运行在 mxflutter 之上。

核心思想

核心思路是把 Flutter 的渲染逻辑中的三棵树中的第一棵,放到 JavaScript 中生成。用 JavaScript 完整实现了 Flutter 控件层封装,可以使用 JavaScript,用极其类似 Dart 的开发方式,开发Flutter应用,利用JavaScript版的轻量级Flutter Runtime,生成UI描述,传递给Dart层的UI引擎,UI引擎把UI描述生产真正的 Flutter 控件。

MxFlutter 目前已经停止维护,具体请看MXFlutter。 MxFlutter通过JavaScript编写Dart,加载线上js文件,通过引擎在运行时转化并显示,从而达到动态化效果。 官方在0.7.0版本开始接入TypeScript,引入npm生态,优化了js开发的成本,向前端生态进一步靠拢。 很遗憾,在对比各大厂的方案时,发现MxFlutter的性价比极低,学习成本也高,而且又抛弃Dart生态。开发及维护成本都很高。

MTFlutter

项目简介

美团的MTFlutter团队flap项目采用的静态生产DSL方案,通过对Dart语言注解,保证平台一致性。实现了动态下发与解释的逻辑页面一体化的 Flutter 动态化方案。Flap 的出现让 Flutter 动态化和包大小这两个短板得到了一定程度的弥补,促进了 Flutter 生态的发展。

核心思想

通过静态生产 DSL+Runtime 解释运行的思路,实现了动态下发与解释的逻辑页面一体化的 Flutter 动态化方案,建设了一套 Flap 生态体系,涵盖了开发、发布、测试、运维各阶段。

布局和逻辑层都使用Dart, 增加了一层语法解析和运行时。然而没有开源动向,无从考察更多。

Fair

项目简介

Fair是为Flutter设计的动态化框架,通过Fair Compiler工具对原生Dart源文件的自动转化,使项目获得动态更新Widget Tree和State的能力。

创建Fair的目标是支持不发版(Android、iOS、Web)的情况下,通过业务bundle和JS下发实现更新,方式类似于React Native。与Flutter Fair集成后,您可以快速发布新的页面,而无需等待应用的下一个发布日期。Fair提供了标准的Widget,它可以被用作一个新的动态页面或作为现有Flutter页面的一部分,诸如运营位的排版/样式修改,整页面替换,局部替换等都可以使用。

核心思想

Fair是58自研的的动态化框架,通过Fair Compiler工具对原生Dart源文件的自动转化,使项目获得动态更新Widget Tree和State的能力。

pic_d3WbXUd1d1V9d1WcXU37U7U75aXdd17b

三,方案对比

经过上述三个方案的调研,我们来大概对比一下上述框架

方案开源方核心思想优点缺点
MXFlutter腾讯用js编写Dart,动态拉取js脚本目前相对最完整的Flutter使用JS开发方案采用js方式编写Dart,维护困难
MTFlutter美团布局,逻辑都使用Dart,增加语法解析和运行时支持布局动态化和逻辑动态化未开源
Fair58通过bundle和js实现热更新支持布局动态化和逻辑动态化开源社区活跃, 开发工具丰富部分语法不支持

可以看到, MXFlutter需要使用js写Dart, 官方已经停止更新,而这种方式我们不能接受, MTFlutter目前未开源,无从继续研究。 接下来着重看一下 Fair

四,Fair接入过程

1,添加依赖

推荐使用pub形式引入

# add Fair dependency
dependencies:
  fair: 2.7.0

# add compiler dependency
dev_dependencies:
  build_runner: ^2.0.0
  fair_compiler: ^1.2.0
 
# switch "fair_version" according to the local Flutter SDK version
dependency_overrides:
  fair_version: 3.0.0

Flutter版本切换

通过切换 flutter_version 版本进行版本兼容。例如,将本机切换为 flutter 2.0.6 后,Fair 需要同步切换

# switch to another stable flutter version
dependency_overrides:
  fair_version: 2.0.6

2,使用 Fair

在App中接入Fair步骤如下:

将 FairApp 添加为需要动态化部分的顶级节点

常见做法是作为 App 的根节点,如果不是全局采用也可以作为子页面的根节点

void main() {
  WidgetsFlutterBinding.ensureInitialized();

  FairApp.runApplication(
    _getApp(),
    plugins: {
    },
  );
}

dynamic _getApp() => FairApp(
  modules: {
  },
  delegate: {
  },
  child: MaterialApp(
    home: FairWidget(
            name: 'DynamicWidget',
            path: 'assets/bundle/lib_src_page_dynamic_widget.fair.json',
            data: {"fairProps": json.encode({})}),
  ),
);
添加动态组件

每一个动态组件由一个FairWidget表示。

FairWidget(
  name: 'DynamicWidget',
  path: 'assets/bundle/lib_src_page_dynamic_widget.fair.json',
  data: {"fairProps": json.encode({})}),

根据不同场景诉求,FairWidget可以混合和使用

  1. 可以作为不同组件混合使用
  2. 一般作为一个全屏页面
  3. 支持嵌套使用,即可以局部嵌套在普通Widget下,也可以嵌套在另一个FairWidget下

五,Fair接入体验

1,fork,下载工程

将官方Github工程fork到自己仓库后, 下载工程。使用官方提供的 test_case/best_ui_templates工程体验fair的体验。

2, 执行 pub get

在 best_ui_templates工程中,执行 pub get命令获取依赖。

3,开发业务

接下来正式开始开发流程。 把一个页面改写为 用Fair 编写:

  1. 创建需要动态化的 componnet, 并添加 @FairPatch() 注解。添加上注解后,在Fair生成产物时,会把此Component build生成 FairWidget加载的产物。

image-20221116173528472

2, 执行 Fair工具链插件的命令生成产物, 如图:

<u>image-20221116173837910</u>

3, 最终生成的产物,拷贝到 assets/bundle目录下(配置config.json后,会自动拷贝)

<u><u>image-20221116182132104</u></u>

4, 看效果, 下图为使用 Fair 改造后的页面:

Screenshot_2022_1116_172859 Screenshot_2022_1116_192940

六,Fair优势

1,社区活跃度高

官方对Fair维护力度大,版本更新较快,问题修复及时,活跃的开发者社区氛围。

使得开发者在开发Fair过程中遇到的问题, 能够及时反馈给官方, 并能得到快速的帮助和解决。

2, 一份代码,灵活使用

Fair的区别于MTFlutter和MXFlutter这2种动态化方案,Fair能让同一份代码在Flutter原生和动态之间随意切换。在开发跟版本需求时,使用原生代码发布,以此持续保持Flutter的性能优势;而在热更新场景可以通过下发动态文件来达到动态更新Widget的目的。使用方式更加灵活。

3,配套开发工具丰富

Faircli配套工具链

官方为了让开发者快速上手,降低接入门槛, 解决在接入过程中的痛点。 Fair团队开发了Faircli配套工具链,主要包含三个部分:

  • 工程创建:快速搭建Fair载体工程及动态化工程。
  • 模板代码:提供页面及组件模板。
  • 本地热更新:线下开发使用,实现开发阶段快速预览Fair动态化功能。

在安装了工具链提供的dart命令行工具及AS插件后, 通过创建模板, 构建产物, 本地启服务,体验热更新功能,开发者可以轻松接入并体验Fair。

Fair语法检测插件

官方为了让开发者在Fair开发过程中,出现不正确或者不支持的语法问题。 开发了配套插件去提示用户使用Fair语法糖。

查看以下示例:

1,build方法下if的代码检测,及提示引导信息

44b58320-e608-420f-854f-799b5bf03cf5image

2,点击more action 或者 AS代码提示快捷键

41094a86-2aea-43e6-b7f0-69aef1c653c0image

3,根据提示点击替换

image.png

通过插件,在编写fair过程中,可以快速识别并解决不支持的语法问题。 提高开发Fair效率。

Fair Web代码编辑器

Fair其中一个方向是在线动态化平台,即在网页中编辑dart代码,在线预览Flutter效果和Fair动态化效果,并且发布Fair动态化产物。

通过在Fair Web代码编辑器,开发者可以在没有复杂的IDE配置的情况下,在网页端开发Fair并预览。 这无疑是降低了接入成本, 为开发者可以快速体验Fair提供了非常便捷的方式。

七,总结

通过近期对各大互联网公司在Flutter动态化方向上的探究方案。 发现这些方案都还没有达到成熟阶段,想在实际业务上落地, 还得看各团队后期的维护力度和开发投入程度。

MXFlutter使用js编写Dart的方式, 抛弃了原本Flutter的开发模式, 导致开发成本大,以及后续维护成本也大,官方已停止维护。

MTFlutter采用布局,逻辑都是使用Dart, 通过静态生产 DSL+Runtime 解释运行的思路,解决布局和逻辑的动态化,然而并没有开源计划,无从深入研究。

Fair通过Fair Compiler工具对原生Dart源文件的自动转化,使项目获得动态更新Widget Tree和State的能力。目前官方维护力度较大, 社区活跃,并且有比较全面的Fair生态工具。 期待 Fair 团队可以解决在开发Fair过程中一些体验问题,如语法支持不全等, 让Fair成为真正能够让开发者可以快速接入,能够达到和正常开发Flutter接近的体验。 为广大Flutter开发人员解决动态化的痛点。

支持我们

欢迎大家使用 Fair,也欢迎大家为我们点亮star
Github地址:github.com/wuba/fair
Fair官网:fair.58.com/

欢迎贡献

通过Issue提交问题,贡献代码请提交Pull Request,管理员将对代码进行审核。