写点什么

带你重新“玩转”Flutter

  • 2021-01-18
  • 本文字数:4915 字

    阅读完需:约 16 分钟

带你重新“玩转”Flutter

Flutter 作为一项已经逐渐进入规模化实践的技术,它的价值已经初步获得认可,后续应该有不错的生命力。作为较早期的 Flutter 实践者,我一直在思考 Flutter 的技术价值以及如何释放这些价值,本篇尝试从一个新的视角去结构化的梳理 Flutter 的技术价值并做对应的应用分析。


这里不会涉及到 Flutter 具体领域的技术点,但是会结合我们团队过去的探索实践,在技术使用策略的层面做一些总结,希望能帮助到小伙伴们在开发实践中思考提炼,抬头看路,仰望星空,找到未来的创新方向。


前端有些啥问题


要溯源 Flutter, 就得从前端说起了。这里的前端是相对于后端的概念,大概泛指通过图形界面实现用户交互的终端技术。这个领域一直很有活力,伴随着互联网一路走来,很多新思路和新技术都有点眼花缭乱:上古的 MVC 已经不大提起了,老一辈的 MVP,MVVM 也逐渐暗淡,新一辈的 Vue,Angular,React 方兴未艾,还有 Redux, Mobx, Hooks 推波助澜。这些技术都像是一个个有感情的小生命,有的热情,有的文艺,有的高冷,十分热闹。也许你会想:它们都在说个啥?它们都想解决一个啥问题?


节点,连接和网络


互联网是这一切存在的大背景,互联网有 3 个大要素:节点,连接和网络拓扑结构。每一个要素的进化都会给前端带来升级和变革:从 PC 互联网到移动互联网再到物联网是节点在进化;充满想象的 5G 时代是连接在进化;从中心化到分布式是网络拓扑结构在进化。前端的使命是让用户(人)高效的嵌入到网络之中,让人和设备融合为一个有生命的网络“节点”。


null


PS:网络模型可以适配到前端的很多场景,里面的“节点”可以代指手机设备,应用程序,进程,页面,甚至组件。


节点与前端技术


前端技术是用户(人)与设备的粘合剂,让二者有机的构成一个网络节点。我尝试剖析下前端技术在节点中的形态:首先它需要提供界面给用户,并响应用户的交互;需要管理和远端节点的通信,交换信息;还需要管理当前节点的信息状态;最后,为了方便的嵌入到网络中,节点需要一个友好的“外观”,就是生命周期。生命周期描述了节点的诞生,消亡,所拥有的权益,和所承担的责任。


null


PS:这个分治模型也具有一定的通用性,可以适用在应用程序,页面,或者组件。


前端要解决的问题


当然,前端技术作为一种软件技术,在日常的工程开发中也需要基础的构建部署支撑。那么,综合前面的讨论,前端要解决的基础要素问题有:


•远端通信

•界面管理

•生命周期

•状态管理

•构建部署


这里面生命周期管理是一个比较“隐形”的问题,它其实就是构建“外观”描述,也就是“组件化”。这些基础要素问题的结构关系大概是:


null


这几个基础要素问题的解决往往不能孤立的进行,在设计解决方案的时候需要彼此配合。前端领域的技术方案通常都会合并解决其中的几个问题,比如:React 解决界面管理和生命周期;Vue 解决了界面管理,状态管理,和生命周期;Redux 专注解决状态管理;GraphQL,BFF,Serverless 解决远端通信;webpack 解决构建部署等等...


那么,Flutter 解决了什么问题?或者,Flutter 可以解决哪些问题呢?


Flutter 都有些啥


有趣的技术方案应该有自己旗帜鲜明的个性,做架构设计往往是遇到了独特的问题或者发现了更好的工具。因此,使用 Flutter 做解决方案的时候,应该梳理下 Flutter 都带来了些什么。一般来说,主要有 Dart 语言,Dart 运行时(VM),GUI 框架,和相关的开发构建工具。


Dart 语言


Dart 语言不好说有多优秀,总的来说是一门工程“友好”的现代语言。也许感觉平淡无奇,但官方在介绍 Flutter 的时候,还略有“骄傲”的展示了它的包容性:它能较为原生的支持声明式,过程式,面向对象,函数式,和响应式等业务场景,它给技术方案实现提供了很自由的范式空间,这是值得发掘和尝试的。结合我自己的实践,有几个点值得一说:


支持类似“协程”处理(async/await/yield):Dart 是单线程的,但是支持异步。这一点需要在使用中去体会,不展开说,理解深了以后,可能对很多问题都有新解法。

提供 Stream:当你要进阶学习 Flutter 的时候,很多地方能见到它。它能做流式数据处理,函数组合,传输控制,属于进阶必知必会。

mixin 特性:这是很有用的特性,尤其在架构设计方面。它提供了新的组合方式,在做功能组合的时候,Dart 可以使用对象注入,或者高阶函数,或者 mixin。


Dart-Runtime / VM


当你引入 Flutter,你就有一个 Dart-Runtime 啦。当然,它本来的意义是支持 Flutter 运行的,然而就像 JS 的 V8 引擎一样,本来是用来支持 H5 页面的,后面的脑洞就越开越大了。当然 Dart 没有 JS 的动态能力(特殊的场合也可以),但它是跨端的,而且 aot 性能有保障,发掘空间较大,还能同时覆盖几乎所有场景,手机 app 开发,PC 的构建部署,服务端,甚至 serverless 运行时。


Flutter 应用开发框架


Flutter 带来了高性能的跨平台 GUI Framework,这没啥可说的,用就是了!但如果已经有了自己的开发生态,舍弃成本太大,又想“借用”下 Flutter 的技术优势,大概有几种方案:


•语言转换,基本上就是把其他语言的布局结果交给 Flutter Framework。

•中层 Framework 介入,选择一棵树介入,这样对接面会小一些。

•底层 Framework 替换,整个替换掉 Flutter Framework, 直接使用底层的 Engine。


null


当然,还有一种方式就是混合开发了,需要实现混合栈管理,而且在 1.22 版本以后,Flutter 升级了 Navigator 组件,提供了 Navigator 2.0,这对混合开发是一个利好。


开发构建工具


值得一提的是 HotReload,谁用谁知道。热部署不仅界面开发需要,这是个通用需求,如果在服务端通过 Dart 实现一个,也是极好的。


Flutter 的构建工具并无太多亮点,而且因为 Dart 语言在 Flutter 中不支持反射(产物太大,增加包体积),业务架构实现上很多需要依赖于编译时处理的技术,这对构建系统的能力有较大依赖的。换一个角度,因为这一领域尚没有成熟方案,加上 Dart 的工程“友好”,可能实现由一种语言来统一前端开发,后端开发,以及构建和部署,真做到了会很酷!


Flutter 可以有些啥玩法


讨论前端要解决的几个基础要素问题,也讨论了 Flutter 带来的技术工具,结合这两点,看看利用 Flutter 都能做些啥。


远端通信


“远端通信”是一个做了抽象的概念,具体形式会根据“节点”的定义不同而不同。可以是指物理设备之间的通信(手机与服务器),也可以指模块之间(页面与另一个页面)的通信,还可以指组件之间(两个 StatefulWidget 之间)的通信。因为在 Flutter 中“一切皆是 Widget”,所以简化的看,Flutter 里面大概分为两种情形:面向服务器的通信和组件(Widget)之间的通信。


面向服务器的通信:这方面有传统的 Restful,GraphQL,还有兴起于“云原生”概念的 Serverless。借助于 Dart 的能力延伸,Dart-Runtime 可以成为 Serverless 的可选容器,那么前后两端都可以使用 Dart 来开发,如果再用 Dart 补足中间的构建部署 pipeline,那么可以搭建出一个很“云原生”概念的应用程序开发流(dev flow):


null


Flutter 的跨端能力结合云原生的弹性部署,前后端的实现可以放到一起,它们之间的调用也变得简单自然,有理由相信这会是一个高效的开发方式。闲鱼已经做了前期的探索,并在业务中尝试落地。


此外,GraphQL 与 Flutter 也是值得尝试的组合,GraphQL 和 React 范式在理念上很对味口,但是我们并没有尝试过。


•组件之间的通信:Flutter 提供的基础业务编程组件是 StatefulWidget 和 StatelessWidget,它们是按树形结构来组织的,并提供了 InheritedWidget 来支持它们之间的通信,在通信方式可以总结出 3 种:


  • Notify 型:通知/监听模式,Flutter 提供了 ValueNotifier 和 ChangeNotifier, 简单方便,适用于轻量信息通信,参见 Provider

  • Invoke 型:接口调用模式,类似轻量 RPC 方式,在 Flutter 的应用架构设计中竟然很少见到。它确实有些重,但是有前面的模式没有的优势:它是双向的。

  • Transmission 型:数据传输模式,Dart 提供了 Stream 来支持这种模式。使用灵活,扩展方便,几乎是框架设计必备,参见 BLoC。


null

状态管理


状态管理是个大问题,它由两个元素构成:状态和处理状态的逻辑。在代码实现上,状态就是数据,逻辑就是函数。当它们变得复杂的时候,解决的办法其实就是拆分,然后再合理的组合。我尝试从状态和逻辑的拆分选择上来列举所有可能的解法:


状态不拆,拆分逻辑: 这种做法的特点全局共用一个状态结构体对象,所有状态全部放在这个对象中,叫做“统一状态管理”。只有一个数据对象,就消除了各个处理逻辑之间信息共享问题,逻辑内部没有状态,变得非常的纯粹(比如纯函数来实现),再将逻辑以合适的方式组合成一个整体,实现上界限清晰,简单优雅,代表的方案就是 Redux。


如果采用这种设计方案,推荐使用函数式风格来实现,这倒不是因为函数式“更高效”,“更优雅”之类的,而是它与函数式的思路十分的契合,在实现上更容易把握住思路。用面向对象来实现也并没有问题,最后的效果取决于你所面临的工程环境和用户。 


这种状态管理好处显而易见:一处状态发生改变,不需要到处发事件同步。但是如果用在复杂大业务上面(比如一个有数十个页面流程的业务产品),状态必定是复杂的,状态结构体必然是庞大的。Redux 的方式很好的管理了逻辑,如果要管理一个统一庞大的状态数据,也许内存级别的 SQL 是个不错的主意,GraphQL-Client 给了我们启发,我们也正在尝试实践。


null


逻辑不拆,拆分状态: 我把这种叫做“步进状态管理”,实际上这类似于状态机模式。但如果要真正使用,状态必须是有限的,而且不能经常变动。这与互联网持续多变的业务需求实际是不符合的,所以采用这种方式的设计几乎没有。但在一些很严肃的业务场景中,比如交易流程,一旦定下来就不容易变动。这时,步进状态管理就很合适了。


null


同时拆分逻辑和状态: 这是容易想到方案,在更细的粒度上,将状态和它对应的处理逻辑拆分打包,变成更小的域(scope),然后统一协调这些子域(subModel),我把这种叫做“组合状态管理”。进一步的方案可以统一定义 subModel 的基础行为,然后引入调度器来协调管理它们,subModel 之间还可以共享上下文来共享信息等等。


这种思路形式自由,可以采用的实现方式很多,经典的面向对象当然不在话下,scoped_model 采用的就是这种思路,scoped_model 实现的简单易懂,同时能力也比较有限。当然也可以采用函数式的方式,高阶函数+闭包也能很好的实现,但是圈内没有看到有相关的设计实现。还有一点,Dart 提供了 Mixin 特性,通过这个特性,可以得到更简洁的实现方案。比如,可以沉淀很多特定的 Model,然后通过 with 选择组合到业务 Model 中来(参考 Flutter Framework 中 WidgetsBinding 的实现)。目前 flutter-hook 在做这方面的探索,flutter-hook 看起来有些迷惑,其核心就是利用了 Dart 的 Mixin 特性来组合状态。


null

界面管理


Flutter 使用了响应式 UI,目的就是让业务开发减少界面的管理工作,只要提供好页面“描述”就行了。虽然 Flutter 内建了类似 Virtual Dom 的 Diff 机制,但是,做这个 Diff 也是要费性能的,如果我们在框架设计上能内建的把 Diff 工作提前到数据层,是不是可以提升性能呢?


我们尝试了几种方法,受制于在 Flutter 中 Dart 不能反射,效果不理想,也许后续生态完善之后会有好的解法。

在 UI 开发中,Flutter 一直有一个隐隐的声音就是动态化,官方好像是“忽略”的,这里也不谈了。


生命周期


Flutter 通过 StatefulWidget 给业务开发提供了基础的生命周期管理。组件生命周期的设计是随着业务场景的不同而不同的,我自己的理解,设计生命周期要从两点出发:一是组件从诞生到消亡的时间线,二是组件在场景中所拥有的权益和所承担的责任。生命周期的扩展原点是 StatefulWidget,因业务场景不同而扩展不同,很难展开讲。 


举个例子,如果使用原生 Flutter 开发(或者叫纯 Flutter 开发),StatefulWidget 所提供的生命周期是足够的。但是如果要做混合开发,引入混合栈后,显然就不够用了。这时候就需要提供新的组件来扩展生命周期,更好的满足混合场景的开发。当时我在做混合栈的时候并没有能理解到这一点。


结语


本篇从分析前端开发需要面对的几个基础核心问题入手,结合 Flutter 带来的技术工具,尝试结构化的分析 Flutter 在业务开发中可能的技术选择和探索方向。当然,技术同学既要仰望星空,还需要脚下看路,对于 Flutter 开发中的热点技术问题,欢迎关注闲鱼技术公众号的其他文章,或者加入我们,和闲鱼一起做一点不一样的技术!


本文转载自:闲鱼技术(ID:XYtech_Alibaba)

原文链接:带你重新“玩转”Flutter

2021-01-18 07:001906

评论 1 条评论

发布
用户头像
目前使用纯Flutter编程的应用还不多, https://apps.apple.com/cn/app/%E6%91%B8%E9%B1%BC-kik/id1549613729 ,这一款应用摸鱼kik是完全使用flutter写的,非常流畅,完全不输原生应用,Flutter大有可为,前景广阔
2021-02-08 16:10
回复
没有更多了
发现更多内容

Libra白皮书解读

程序那些事

区块链 facebook 数字货币 libra

Libra教程之:Libra协议的关键概念

程序那些事

区块链 libra blockchain 协议

LeetCode 756. Pyramid Transition Matrix

liu_liu

LeetCode

[转载]Go 和 Java的15个主要差异

卓丁

Java Go 语言

互金总结系列(1)--开篇

互金从业者X

读《你的灯还亮着吗》

liu_liu

读书感悟

架构师训练营第一周作业

Benjamin

Kafka零数据丢失的配置方案

奈学教育

kafka

如何用日记提升写作能力?

石云升

学习 方法 写作

ARTS WEEK3

紫枫

ARTS 打卡计划

大中台模式下如何构建复杂业务核心状态机组件

奈学教育

中台

由一次管理后台定时推送功能引发的对RabbitMQ延迟队列的思考(一)

LSJ

Java RabbitMQ 延迟队列

游戏夜读 | 如何面对前景渺茫?

game1night

白话说流——什么是流,从批认识流(二)

KAMI

大数据 flink 流计算

你不能不掌握的软技能——业务语言

KAMI

方法论 开发 沟通 软技能

k8s 上运行我们的 springboot 服务之——自动化测试

柠檬

maven DevOps Unit Test

[翻译]The Go Blog《Go maps in action》

卓丁

hashmap map 哈希表 Go 语言

SpringMVC中Http请求方式转换(post转换为put/delete等方式)

知春秋

springmvc post post到put方式请求 post到delete方式请求

小师妹学JavaIO之:NIO中那些奇怪的Buffer

程序那些事

io nio Java 25 周年 小师妹 buffer

算法基础:排序算法看这一篇就够了

Geek_k6ry2n

排序算法

原创 | TDD工具集:JUnit、AssertJ和Mockito (二十一)编写测试-动态测试

编程道与术

Java 编程 TDD 单元测试 JUnit

JVM学习笔记——JVM类加载机制

王海

Java 面试 JVM

如何基于 OAM 编写一个扩展 Trait?

钱王骞

云原生 k8s OAM

食堂就餐卡系统架构设计文档

dony.zhang

[架构师训练营] Week01 - 食堂就餐卡系统设计

谭方敏

学习

拙见/ 什么是自驱力?

ZoomQuiet大妈

自我提升 大妈 是也乎 IMHO 蟒营®

《Golang工具go doc使用透析》

卓丁

godoc go doc 源码阅读 Go 语言

B端产品经理养成记(4):敏捷项目

涛哥 数字产品和业务架构

敏捷 产品经理

大中台模式下如何构建复杂业务核心状态机组件

古月木易

做产品少走弯路:上帝视角(2)

我是IT民工

产品 方法 路径 知识体系

公司治理的两个关键要素:存在的基石 + 成长的飞轮

霍太稳@极客邦科技

发展 公司管理 增长

带你重新“玩转”Flutter_大前端_闲鱼技术_InfoQ精选文章