写点什么

带你重新“玩转”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:001915

评论 1 条评论

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

逐梦航天—数字孪生技术仿真火箭发射!

ThingJS数字孪生引擎

大前端 物联网 可视化 航天 数字孪生

LeetCode刷题283-简单-移动零

ベ布小禅

9月日更

关于takin-data,你想知道的都在这里(一)启动命令篇

TakinTalks稳定性社区

用友YonSuite在基于六力模型的SaaS产品中位居业界前列

海比研究院

MVP验证方向,差异化策略超越竞争

石云升

MVP 9月日更

小游戏 合成

游戏开发_软件开发

关于比特币和区块链的3件重要事项

CECBC

Premo测试框架详解

趣链科技

区块链 测试工具 测试发开

TLS协议分析 (二) 架构总览

OpenIM

浅谈实时语音质量监控系统

声网

音视频

为什么说敏捷开发是应用程序的未来?

优秀

敏捷开发

卖NFT表情包赚上百万,区块链技术终于找到了真正价值?

CECBC

做百度AI工程师,还要会“相牛”?

百度大脑

人工智能

NeonIO 云原生存储简介与应用

QingStor分布式存储

云原生 分布式存储

关于takin-data,你想知道的都在这里(二)trace日志篇

TakinTalks稳定性社区

♟Go语言那些事儿之Redis连接与数据操作♟

Regan Yue

Go 语言 9月日更

从 ClickHouse 到自研 ByteHouse:实时数据分析场景下的优化实践

火山引擎开发者社区

Clickhouse

从电视购物到电商直播,什么造就了「带货的参差」

融云 RongCloud

自动交Y机器人开发|市值机器人源码搭建

量化系统19942438797

21年字节+美团+腾讯,大厂必问面试真题总结(Java岗)

Java架构师迁哥

执行update语句,用没用到索引,区别大吗?

Simon

MySQL 索引

架构实战营模块7课后作业

燕燕 yen yen

架构实战营

【LeetCode】数据流中的中位数Java题解

Albert

算法 LeetCode 9月日更

没项目经历,面试有点怂....

Java架构师迁哥

【墨天轮专访第三期】达梦数据库冯源:丢掉幻想投入战斗,国产数据库的机遇窗口已经来临!

墨天轮

数据库 国产数据库 达梦

学生管理系统详细架构设计

Nullrable

三涧溪村:乡村产业插上数字化翅膀

工业互联网

学习笔记:TCP传输控制协议(一)

姬翔

9月日更

金融科技成为服贸会热议话题:数字化转型中如何保障金融安全

CECBC

tomcat启动失败常见错误

hasWhere

网络攻防学习笔记 Day128

穿过生命散发芬芳

开发安全 9月日更

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