您好,感谢收听。这是 It's All Widgets Flutter 播客。我的名字是 Solo Korn。每一期节目,我们都有机会与 Flutter 社区的另一位优秀成员交谈。在本期节目中,我们非常幸运地能够与 Rohde 交流。欢迎。嘿,你好吗?很好。感谢你的参与。那么,首先,你能分享一下你的背景吗?是的,当然。我从 Flutter Beta 3 开始使用 Flutter,时间很长了,但我一直对参与这段旅程感到非常兴奋。
我现在在 Google 的开发者解决方案团队工作,这意味着我们正在进行跨开发团队的工作,能够帮助许多团队一起利用多种产品。其中包括 Flutter。所以我试图让人们对使用新技术感到兴奋,并试图弄清楚如何使其适用于他们的工作流程。太棒了。正如你提到的,你从 Beta 版就开始使用 Flutter 了。你第一次是怎么听说它的?你还记得吗?
是的,我在我工作的第一家公司是一名 C# .net 开发人员。我们当时正在做一家抵押贷款公司的软件,开始构建原生 iOS 应用程序。这很有趣。Switch 实际上是我的第一门语言。所以我当时正在学习原生开发。
就像很多人面临的情况一样,市场需要一个 Android 应用,而我没有 Android 应用。所以我考虑原生构建一个。之后,由于我是一个单人开发团队,同时构建两个应用变得非常困难。所以我们做了以下尝试:我探索了 React Native。我当时探索了 Capacitor 或 Ionic。我还探索了……
Flutter 是我能快速运行、原生运行的唯一解决方案,并且我对热重载印象深刻。我还不需要接触 JavaScript,虽然你知道,我是 JavaScript 的忠实粉丝,但是……
它是一个,你知道,来自 C# 的 Dart 实际上非常容易上手,事实上,当时 Android Studio 可以让你复制粘贴 Java 代码,它会将其转换为 Dart,我认为这非常有趣,反之亦然,但是……
是的,这实际上是关于寻找一个能够帮助我提高速度的解决方案。通过拥有原生 Swift 和 Objective-C 以及 Kotlin 和 Java 的经验,我能够非常快速地为 Flutter 制作许多插件。这实际上是在 SwiftUI 和 Jetpack Compose 出现之前,所以这就像在 Kotlin 和 Swift 的早期阶段,这很有趣。所以……
之后我做了一系列软件工程工作,然后是开发者关系工作,然后去了 Google。然后在加入这个团队之前的三年里,我一直在从事 Material Design 的工作,也在那里帮助 Flutter 的事情。
很好。对于收听的各位,如果你想了解更多关于 Rody 的故事或他的背景,我们大约三年前录制了一期节目,我们会在节目说明中添加该节目的链接。我邀请 Rody 回来参加第二期节目的原因是因为他正在开发的新软件包。它叫做 Signals。我一直密切关注它。我对此感到非常惊讶。你想花一些时间讨论一下吗?解释一下你为什么创建它以及它的一些好处?
是的,当然,我很乐意。所以,如果你不熟悉 Signals,这是一个我开始探索的新库。我一直在 TypeScript 和 JavaScript 世界中工作,因为我一段时间以来一直在帮助 Lit 团队,我观察到的一件事是 Signals 某种程度上
出现,变得非常流行,然后很多人开始集成它。这包括 Angular、SolidJS、Quick、React,等等。如果有一个 JavaScript 库,或者在 Svelte 中,如果你能说出名字,那么可能就有一个 JavaScript 库在使用它。现在,有一些像 Vue 这样的框架以及许多其他框架都使用了 Signals,Signals 绝不是一个新概念。但是 Signals……
席卷了这个世界。如果能让 JavaScript 框架在任何事情上达成一致,那么一定有一些神奇的事情正在发生。所以它始于观察,做一些例子。而且,你知道,我天真的想法是,这与 ValueNotifier 有什么不同?我们已经有了一个可以响应式监听的 get 和 set 值。
所以我当时并没有多想。直到后来社区中的一些人开始深入研究一些东西。我开始研究一些不同的源代码。这是一个更直接的例子。所以我通读了整个源代码。当我这样做的时候,我意识到,等等,这里有一些东西,我要尝试把它移植到 Dart。
我做了这件事,这真的很酷。我很兴奋,但这并不惊人。这就是 Dart Signals 的第一个版本,我当时只是把它叫做 Dart Signals。然后我几乎完成了。我正准备转而做其他事情。但后来我想起了 Preact 对 Signals 做了一些不同的处理。
Preact 由 Jason Miller 进行了大量的开发,他以前也在 Google 工作。所以我记得他们以一种可以被许多框架采用的方式制作了 Preact Signals,而不需要在其中包含框架特有的功能。这引起了我的兴趣。这是一个单文件实现,我认为这也很重要,因为它没有任何依赖项。所有代码都在那里。所以我做了以下操作:我使用了我的实现,
我开始将其移植到 Dart,我能够完整地移植它,并编写了许多测试以确保它都能正常工作。当然,我发现了一些
你知道,我遗漏的一些字符。但是一旦我得到了它,它就非常棒了。Preact 实现与所有其他实现相比,真正酷的一点是它使用双向链表作为数据结构。几乎所有 Signals 通常都会使用数组或集合来存储其依赖项。我会详细介绍一下
因此,它能够只存储其源和目标的引用,甚至在垃圾收集器之前就删除尚未更新、监听或处置的 Signals。这意味着当您第一次运行应用程序时,
它几乎会非常快速地稳定内存使用量,并且在剩余时间内保持这种性能。它有一个非常酷的机制来实现这一点。但是,无论如何,所以在构建之后,它就是将我在 Preact 中移植的 Dart 反应式编程带到 Flutter。所以我做的第一件事是将软件包命名为 PreactSignals。
当然,这对于 SEO 来说非常令人困惑,因为人们不知道 Preact 是什么。Preact 在 Flutter 领域毫无意义。但我想忠于这个实现。无论如何,我查找 Signals 软件包。
我看到它已经有 10 年没有更新了。所以我当时想,哦,好吧,有人拥有这个名称,没关系,随便吧。然后我第二天又查看了 GitHub 仓库。结果发现,拥有该名称和实现的人实际上是我在 Google 的上司。所以我给他发了消息,他
转让了所有权,然后当然更新了版本。这真的很令人兴奋。从那时起,我开始为 Flutter 制作反应式部分。我认为这是非常有趣的部分,因为它开始感觉像魔法一样。我认为我已经使用了所有可能的 state management 了,我可能已经使用几乎所有解决方案发布了生产应用程序。
如果我没有发布它,我也会广泛地探索它。所以我非常喜欢了解社区正在做什么。所以对我来说,最重要的原则之一是,你知道,就像,好吧,我不只是想构建另一个 state management 库并做其他人都在做的事情。我想做一些新颖且不同的事情,并真正节省时间。所以,你知道,最小的 API 是,
我特别想做的一件事是专注于编写 Dart 代码,而不是样板代码或框架代码。我想为 Dart 创建一个反应式系统,它由 Flutter 增强,但你可以在 Dart Web 应用程序中使用它,你可以创建一个反应式 CLI,你可以使用 Dart 到原生,任何东西都是一个
可能的候选者。所以当我开始使用 Flutter 时,我创建了,我首先使用一个名为 .watch 的扩展方法。它会做什么呢?它会查找最近的树并重建最近的元素。这可以是一个单个 widget,也可以是一个 builder。这真的很酷。但是有一些情况需要
需要涵盖,社区实际上已经开始为 Signals 仓库做贡献,我认为这真的很酷,人们也为此制作了内容。所以人们做的第一件事之一就是审查,哦,你知道,这可能可以更好一些。所以我探索了构建一个 watch widget。watch widget 是 builder widget 的直接替代品。
它允许你,如果你只想包装整个 widget,你可以这样做。但它提供了超细粒度的反应性。你可能会想,我已经可以使用 ValueListenableBuilder 做到这一点。我已经可以使用所有这些方法做到这一点。但是使用 Signals,你不需要说明你正在使用哪些依赖项。你也不必每次都使用它们。
与 hooks 和其他一些状态库不同,你可以在条件中使用它们。你可以对它们做出反应,你知道,可能在不同的分支上。你可以使用 switch 模式匹配。你可以在 builder 方法中使用一个或十个 Signals,以及更多。我实际上构建了一个基于节点的编辑器,我认为如果你能查看一下会很酷。这表明你可以拥有,比如,
图中的循环依赖项相互连接,但这非常强大,然后在那之后,我还构建了一个 listen 方法,它与 watch 方法相同,但它不会重建 widget,而只是使用回调做出反应,但通过这样做,仅仅通过拥有这三个基本元素,我能够涵盖所有我需要的 Flutter 反应式编程的情况,现在……
与其他一些系统相比,Signals 的最大优势是两件非常重要的事情。第一部分是 Signals 是一个同步状态库。许多其他解决方案充其量是 future,但通常是异步的。这意味着你将
写入一些值,你将调用网络请求,你将拥有调用其他函数的函数,你将拥有更新的所有流,等等。但是你的 UI 可能会在每一帧中渲染,并且需要是同步的。因此,你始终需要显示正确状态。但不仅如此,因为它是一个同步库,你可以使用它进行动画,你可以进行
可预测的更新,因为你知道图中没有任何地方需要等待另一个信号。它就像基本上在微任务队列中运行一样。现在,另一个重要部分是 Signals 是一个基于拉取的系统,而不是基于推送的系统。你可能不知道这意味着什么。简而言之,如果你考虑一下 ValueNotifier 或 Bloc,例如,
当你更新一个值时,你正在更新时触发所有计算。因此,当你调用 setValue 时,它将遍历其侦听器并通知它们,如果这些侦听器有侦听器,那么这也会传播并更新它们。但这有,你知道,它自己的优点,但我认为最终会导致许多你不必做的工作。
使用 Signals,你可以根据需要多次更新信号值。你可以每秒更新它一千次。但是很酷的是,computed 是 Signals 的另一个基本元素,它允许你引用任意数量的 Signals 并返回一个值。现在这是一个回调。假设你有一个每秒更新一千次的信号。你有一个计算值来监听一个条件并返回一个值。
现在,这个计算值可能每秒只运行几次,甚至可能只运行一次,这取决于条件。然后你甚至可以拥有另一个计算信号链来监听它。但是真正神奇的是,如果你从未读取计算信号,那么这些回调实际上永远不会被调用。所以当你考虑你的系统中的状态以及你的 UI 状态、你的应用程序的状态时,
通常,你将拥有诸如用户信号之类的东西,它将绑定到某些身份验证回调,并且该用户信号将经常更改和更新。
你将拥有数据库查询,你将拥有网络请求,你将拥有诸如网络是否活动之类的条件,你可能会使用诸如这个人是否有权访问付费计划之类的条件,你将拥有所有这些断开的信号,它们以自己的频率更新状态。但是你的屏幕不一定会仅仅是始终引用所有可能的信号的实用程序。
你将派生状态,并且你像查询一样,我想显示的不是所有待办事项,而是具有这些值的已过滤的待办事项集。而且,你知道,computed 通常允许你描述你想要渲染的内容以及尽可能少的元素。这意味着,你知道,它也会缓存其值。所以,你知道,你可以每帧读取它,它不必重新计算。
假设你在树中或链中有很多计算信号,因为它不是树。只有当它的父级(在其依赖项中)发生更改时,它才需要重新计算其回调,并且它不必继续传播。所以这真的很……
使它真正高效。我认为这真的很酷,因为 Signals 是一个断开的图。没有根信号。没有传播它的根树。你不需要用根 widget 包装你的整个应用程序。它只是真正允许你构建几乎你能想到的每一个状态基元。还有其他辅助程序,例如 batch,它允许你在单个状态中更新多个信号。
回调效果,watch widget 和 listen 就是基于此构建的。这允许你订阅一个信号并触发更新的回调。除了我所做的核心 Signals 之外,我真的开始为许多常见的辅助程序编写语法糖。
这包括诸如 setSignal、map、list 之类的值信号,还包括诸如 futureSignal、streamSignal 和 asyncSignal 之类的东西,它们使用异步状态来表示信号可能处于的所有可能状态。人们通常问到的最重要的事情之一是,好吧,我该如何进行异步计算?我认为这是关于 Signals 的最大误解之一,
你创建的不是你在读取时解析的异步图。你创建的是一系列表示随时间变化的值的信号。你根据该派生状态查询该数据。你应该做的是使用效果来触发回调到异步函数,这些函数会触发对信号的新更新,依此类推。然后,你还可以使用诸如 featureSignal 之类的东西来对值做出反应,但是
Signals 是同步的,我认为这正是真正的魔力所在。但我知道这已经让一些人感到困惑,你知道,这完全没问题。它不同。但是是的,我一直非常喜欢它。我开始以许多不同的方式探索它。所以我谈到了基于节点的编辑器,但它是我一直想构建但没有合适的 state management 库来构建的一个很好的例子
使用,你知道,循环图,你知道,相互连接的东西,所有东西都在更新,你知道,可能,你知道,数千次,使用 Signals,我实际上终于能够制作演示了,而且它真的很酷,因为你可以拥有更新的信号值,然后它们以链的形式连接起来,我甚至能够制作一个全加器,全加器是一个非常有趣的逻辑门等等,所以
是的。但是,你知道,总而言之,我认为 Signals 也是社区拥有的其他解决方案的一个很好的补充。例如,你可能首先尝试做的事情是使用它替换所有依赖项注入。虽然我认为它可以用于此目的,但我认为我个人使用 get_it 和许多。
我所做的事情,然后我将我的数据库提供程序向下传递到树中,然后我在全局或局部 widget 中创建 Signals,但是像 Angular 那样的一些东西一直在使用 Signals 做一些非常酷的事情,他们使用,你知道,他们长期以来一直在使用 RxJS,他们将 Signals 与 RxJS 结合起来实际上非常酷,因为……
RxJS 是一个很好的反应式示例,并声明性地说明了随时间推移状态会发生什么。Signals 非常适合构建 UI、读取值和更新值。所以我移植了他们的一些扩展方法和连接器,但是你绝对应该查看一下。但是我在文档站点上真正酷的一件事是 RxDart 和 Signals 移植。
它展示了如何将它们连接在一起。我认为你会发现它们是一个很酷的组合,因为它允许你编写非常易于响应的代码。我认为这是真正好的部分。我还做了一些其他事情,无论是开发工具还是添加诸如观察者之类的东西。但是真正的魔力归结于我将如何为这个屏幕构建状态的最佳方法?
我发现 Signals 提供了能够表示我试图执行的所有不同类型和排列的不错的基元。它可以非常简单,也可以非常复杂,但你可以免费获得这种性能最小的更新和缓存。你可以像我说的那样,从围绕你的整个 widget 树的 watch 开始,然后
如果你有一个想要为其创建边界的子树部分,你可以用它自己的 watch 包装它,它将创建一个边界。但这真的很、很、很、很有趣。我强烈建议你查看一下。有一个 GitHub 仓库,github.com/rodydavis/signals.dart,你可以查看许多示例。
我还一直非常努力地查看讨论。已经有许多社区反馈,并将继续这样做。所以请深入研究,我很乐意听到你对这些事情的想法。如果只是,你知道,
你对 JS 感到好奇,例如为什么 Signals 如此流行,为什么我们要在 Flutter 中这样做?这有点像我为什么专注于为它制作更多内容的原因。所以,如果你在那里需要具体的东西,请通过 Twitter 或其他方式与我联系。但我认为有很多很好的机会可以重新审视 Flutter 如何进行状态更新。而且,你知道,Flutter 中的 state management 一直以来都很有趣,但我认为这是因为,你知道,有很多系统能够在它的一方面做一些非常酷的事情,但是你到达一个点,就像,好吧,这部分效果不好,或者这需要太多的样板代码,或者这与我想要编写的内容太不一样了……
但我记得我们之前谈到过,你知道,有一些很酷的事情导致了 Signals 的发展。有像 Knockout.js 这样的东西。在此之前,有真正古老的基于可观察对象的 Signals。Solid.js 实际上吸收了很多这些内容,并且,你知道,站在巨人的肩膀上,创建了一个非常酷的 Signals 库。
它也依赖于……我知道我认为他们现在已经使其更容易在框架之外使用,但是……Preact 吸收了 Solid 所做的工作,并研究了社区的其余部分所做的工作,并且能够更上一层楼,我认为这就是为什么随着时间的推移会发生这种演变,就像你知道,因为我的第一个想法是为什么现在才发生,但我认为……
有很多事情导致了这一点。因为我们现在在这里,我认为这是一个非常酷的时机……如果你有一段时间没有更新你的 state management,或者正在寻找新的解决方案,我强烈建议你查看一下。因为就像许多其他人所说的那样,包括我自己,我感到非常……
惊喜。对于尝试以极简的方式构建反应式状态而无需代码生成或需要扩展许多框架特定内容来说,这就像一股清新的空气。我认为虽然所有这些都有其用途并且令人难以置信,但我认为能够编写更好的 Dart 代码是一个不错的部分。
这是一个很棒的解释。非常感谢。所以在我还有你的时间里,你对刚开始使用 Flutter 的新开发者有什么建议吗?当然。嗯,在我职业生涯早期帮助我的一件最重要的事情是,嗯,参与其中,无论是……Discord 还是……Slack,我认为他们现在都迁移到 Discord 了,但是……
在 Twitter 和其他平台上找到并与真实的人联系对于尝试找到可以向其提问的社区成员来说非常重要。还有一些很棒的东西,例如 Hump Day,Flutter 社区真的很棒。所以,你知道,尝试跳进去和人们交谈。尽早建立这些联系实际上是一种很好的方式来
了解构建这些库的人是真实的人,你知道,在另一边。有了这个,你知道,另一件重要的事情是我真的很喜欢阅读代码。
来理解一些东西。你知道,文档是次要的。我认为文档和代码是一个非常好的选择。所以我深入研究了 Flutter 框架和 GitHub,并查找了许多内容。你会惊讶于有多少 Dart 和 Flutter 仓库在那里,它们只是有一些令人惊叹的代码片段
非常酷的例子……我现在要提到的一个例子是……如果你从未见过 Dart 团队的 async 包,它是一个非常酷的……隐藏的宝石……或者 retry 包,这是另一个具有指数退避的包,等等,但是真的尝试尽你所能……找到人们……另一件对我来说有用的事情是……
当我开始的时候,没有很多软件包。我的意思是,仍然有很多,但不像现在这么多。所以对我来说,这就像尝试找到现有的东西,阅读源代码,并尝试在发现缺少东西的地方提供帮助和贡献。我建议如果你……
不是对 Flutter 非常陌生,而是最近才开始使用 Flutter,那么请深入研究并尝试查找你喜欢的任何可能需要一些拉取请求或只是通过一些小事情来提供帮助的软件包。做出贡献是使 Flutter 变得如此出色的一种方式。
而且,你知道,让它成为开源是一种非常酷的方式,我们可以互相帮助并构建一些很酷的示例。许多构建软件包的人都是一个人,你知道,他们可能正在构建它,因为他们有一个生产应用程序,他们想要他们已经做过很多次的这一个部分来帮助社区。所以你可能会发现一些一年没有更新的软件包。
这并不意味着它们已经死了,但我认为,你知道,现在是尝试……与他们联系并……提供拉取请求并尝试……推动发展的好时机。嗯,然后如果……你需要示例,不要害怕提出要求。嗯,因为社区中可能有人……会喜欢构建它。嗯,
我会说,如果你更高级并且来自 JavaScript 或 iOS 或 Android,我强烈建议你查看为 Flutter 构建插件,因为利用你的原生代码经验实际上会使它成为一个非常棒的软件包。所以这是帮助我的另一件非常重要的事情。很好。这是很好的建议。Rody,再次感谢你参加播客。感谢收听。下次再见。太棒了。谢谢。谢谢。