为什么不试试 ReactiveUI 呢?

RxUI 是一个强大的 MVVM 框架,那么它适合新手吗?之后我会制作这方面的教程吗?

在去年,我制作了一期视频与大家探讨响应式编程(Reactive Programming)。在视频的结尾,我也“剧透”了自己或许会在将来的一天继续深入这个话题,与大家探讨使用 ReactiveUI(以下简称为“RxUI”)这个库来做符合 MVVM 模式的桌面应用。

但现在已经过去很久了,后续内容迟迟没有出现。这真的是一件尴尬的事情。事实上,这个话题可谓难度非常高,尤其是相较于其他 MVVM 框架(比如 CommunityToolkit.Mvvm、Prism 等)来说,它的入门难度极高。

最近看了一篇列在了 ReactiveUI 官方文档的“资源”中的博文:I have become a huge fan of ReactiveUI。有趣的是,当你点开这篇文章后,你会发现标题是“Don’t use ReactiveUI”。不过作者立刻在第一段就澄清了这只是一个颇具欺骗性的标题,实际上并不是想表达这个意思,作者是在说反话。

所以我的这篇文章也仿照这一点,起了一个颇具欺骗性的标题——是的,我也在说反话。现阶段我不推荐任何人使用 RxUI,尤其是现在有 CommunityToolkit.Mvvm 这样成熟且易上手的框架的情况下(我还专门写了一整个 入门教程 来和大家分享这个工具包的使用)。

为什么不推荐使用 RxUI?

那么为什么我不推荐使用 RxUI 呢?

如果你或你的团队选择了 RxUI 来进行开发,那么你将获得下面几个优势:

  1. 你的代码会看起来很酷;
  2. 你会获得新颖而不同寻常的开发体验;
  3. 你的代码会非常难以让新入职的同事接手,这会让你变得更加重要,也更难以被“优化”;
  4. 你会充分培养自己的自学能力——因为你几乎无法靠别人理清楚你到底在干什么。

是的,如果你选择了 RxUI,那么“you are on your own.”,甚至包括目前最聪明的几个大语言模型,比如 ChatGPT o3、Claude 3.7 Sonnet、Gemini 2.0 Pro、Grok 3、DeepSeek R1 等,也帮不了你。现在不能,将来应该也不能,因为它们的训练集里面并不充分包含这么一个技术。

可能有些开发者会说,RxUI 有你说的那么难吗?我觉得还好啊,不就是借助 RaiseAndSetIfChanged 这个方法来实现属性的通知,用 ReactiveCommand 来创建用于绑定 Command 的命令,最多再用 ObservableAsPropertyHelper(OAPH)来创建一个只读属性(或者说计算属性),就可以了啊?如果嫌麻烦,还可以借助 Fody,或者源生成器不是嘛。

如果你这样想,那么恭喜你,你可能只是刚刚入门,并且你的项目并不复杂,仅靠入门的这些知识就可以应付。如果你深入了解 RxUI,你会发现你要考虑的因素实在是太多了,并且很多时候你可能都不知道自己在做什么,该想什么,以及该如何判断某种做法的优劣。

不相信的话,不妨问自己这么几个问题:

  • 你知道视图及视图模型的生命周期,以及什么时候该使用 WhenActivated 吗?
  • 你知道哪些对象应当考虑资源回收的问题吗?
  • 如果你不确定某个对象是否应该显式释放资源,你知道该如何去判断吗?
  • 你知道 WhenAnyWhenAnyValueWhenAnyObservable 这三个方法的区别以及触发时机吗?
  • 你知道什么时候该使用 BindBindCommand 等方法而不是直接在 XAML 中绑定吗?
  • 你知道 MessageBusDynamicData 这些类的作用吗?
  • 你知道如何处理可观测对象的异常吗?
  • 你知道如何取消一个从可观测对象或异步任务创建的命令吗?
  • 你知道如何正确搭配 IoC 容器来使用 RxUI 吗?你知道 RxUI 所依赖的 Splat 吗?
  • 你熟悉响应式编程吗?

最后一个问题应该才是灵魂拷问。如果你没有拿下响应式编程的信心,那么我建议你还是先放弃 RxUI,并且也基本上不要指望可以一边学习 RxUI 一边入门响应式编程,因为响应式编程是一种思想,而这个思想在 RxUI 中只体现了一方面。但 RxUI 还需要学习的东西还有很多,比如如何遵循 MVVM 模式,如何正确使用依赖注入,如何管理视图的生命周期,如何在界面框架中去实现绑定等。如果你一边学习一边开发,那么很有可能随着你慢慢理解这一切,你会发现你之前写的代码是多么地糟糕。

当你度过重重困难,最终会得到什么?

好吧。假如你历经千辛万苦,终于熟练掌握了 RxUI,那么你会得到什么呢?或者说,如果你打一开始就选择了 CommunityToolkit.Mvvm,你会损失什么?

首先,你可以优雅地实现一个搜索框,这也是 RxUI 官方示例中经常展示的场景:用户输入文字,搜索结果实时更新,并能有效处理延迟、去重等复杂逻辑;此外,如果有一些属性及命令,它们之间的通知关系比较复杂,那么 RxUI 会让你的代码更加直观,好比于从大家的主动通知目标变成了目标去主动观察大家;更重要的是,你还入门了响应式编程这一个有趣的概念。

然而,这种便利并非毫无代价。你可能会为了掌握 RxUI 付出大量的时间和精力。更重要的是,你的代码库可能会变得难以维护,因为其中充斥着 RxUI 特有的 API 和概念。这些 API 本身就带有一丝“黑魔法”的色彩,可能会让团队中的其他成员,甚至未来的维护者感到困惑——掌握这门框架的人才,多吗?

遇到了困难,你去看官方文档,去问群友,去问大模型,恐怕都不会有太多的帮助。至少我并不认为,RxUI 这么复杂的框架,它的官方文档足够详尽和易懂,我也基本上找不到什么稍微深入一些的示例项目,更是几乎看不到有多少人在写关于 RxUI 的博客——那该如何指望大语言模型能够掌握这个技术呢?

但如果你选择了别的框架,那么相关的教程实在是太多了,毕竟 MVVM 教程多得是,无非就是了解一下框架的用法,它们的视图模型的基类是如何实现 INPC 接口,并提供了一些额外的辅助方法的,就差不多了。这么直白的学习路线,是 RxUI 所不拥有的,因为后者需要更多的背景知识。

哦对了,差点忘了一件事情:如果你选择了 RxUI,你还有可能获得一个“四不像”的项目。因为 RxUI 为了实现 MVVM,将整个响应式编程的概念引入到了项目中,但是这一概念一般情况下我们根本用不到。因此,很多人即便在自己的项目中使用了 RxUI,但是对于其他的业务逻辑,仍旧使用的是传统的思路,比如使用多线程加锁(好一点的用信号量,差一点的甚至还在用 bool 类型的标志位)、线程安全的队列、LINQ 甚至是 for 循环,而完全不考虑 Rx 提供的数据流这一概念。

此外,RxUI 还引入了 Splat 这个库,用于解决跨平台的问题(比如读取本地图片等),还提供了一套简单的 IoC 容器——不管你用到用不到,它都在那里。作为一个完美主义者,我非常不喜欢引入一个库,但是只用了一小部分功能这件事情。因此我更喜欢 CommunityToolkit.Mvvm,因为它只提供了 MVVM 的基础功能,我可以随意搭配其他我想用的 IoC 容器等(同理,我也并不怎么喜欢 Prism)。

因此,权衡利弊至关重要。如果你需要处理极其复杂的响应式场景,并且愿意投入大量时间和精力去学习和维护,那么 RxUI 或许是一个不错的选择;但如果你的项目复杂度适中,或者团队成员对响应式编程不熟悉,那么选择更易上手的框架可能更为明智。毕竟,技术的选择应该服务于项目的目标,而不是反过来。

我还打不打算讲 RxUI 了?

这是一个充满矛盾的问题。一方面,我写了这么多来“唱衰”RxUI,劝退新手,似乎已经给它判了“死刑”。那么,我还会继续深入讲解它吗?

我想,答案是肯定的。尽管入门门槛高,维护成本大,但 RxUI 终究是一门有趣的技术,一个有着十几年历史却依然“新颖”的框架,一个能将不畏挑战的开发者筛选出来的概念。能够攻克它,掌握它,并将经验分享给大家,这本身就是一件充满成就感的事情。

当然,这一天恐怕不会很快到来。首先,我对响应式编程本身的理解还需要进一步加深。其次,如果仅仅是重复介绍 RaiseAndSetIfChangedReactiveCommandObservableAsPropertyHelper 这些基础用法,那毫无意义。市面上并不缺乏这种级别的教程。真正有价值的,是解决前面提到的那些深层问题,帮助大家真正理解和掌握 RxUI 的精髓。否则,互联网上只会多出一篇平庸的“教程”,而不是一篇真正具有“教育意义”的文章。

所以,我真心期待有读者能在评论区“打脸”,分享你优秀的 RxUI 学习路线和实践经验。这不仅能让我受益,更能帮助所有对 RxUI 感兴趣的开发者。如果能看到更多人分享他们成功使用 RxUI 的案例,那将是对我观点最好的反驳,也是我最乐于见到的。毕竟,技术的世界,永远欢迎不同的声音和观点。

使用 Hugo 构建
主题 StackJimmy 设计