关于代码生成
代码生成是使用工具为我们生成代码的想法。 在 Dart 中,它的缺点是需要额外的步骤来“编译”应用程序。 尽管这个问题可能在不久的将来得到解决, 但 Dart 团队正在研究这个问题的潜在解决方案。
在 Riverpod 的上下文中,代码生成是稍微改变定义 "provider" 的语法。例如,代替:
final fetchUserProvider = FutureProvider.autoDispose.family<User, int>((ref, userId) async {
final json = await http.get('api/user/$userId');
return User.fromJson(json);
});
使用代码生成,我们可以编写:
Future<User> fetchUser(Ref ref, {required int userId}) async {
final json = await http.get('api/user/$userId');
return User.fromJson(json);
}
使用 Riverpod 时,代码生成是完全可选的。 没有的话完全可以使用 Riverpod。 同时,Riverpod 支持代码生成并推荐使用它。
有关如何安装和使用 Riverpod 代码生成器的信息, 请参阅入门指南页面。 确保在文档的侧栏中启用代码生成。
我应该使用代码生成吗?
Riverpod 中的代码生成是可选的。 考虑到这一点,您可能会想是否应该使用它。
答案是:很可能是的。
使用代码生成是使用 Riverpod 的推荐方式。
这是一种更面向未来的方法,可以让您充分发挥 Riverpod 的潜力。
与此同时,许多应用程序已经使用
Freezed 或 json_serializable
等包来生成代码。在这种情况下,您的项目可能已经设置为代码生成,
并且使用 Riverpod 应该很简单。
目前,代码生成是可选的,因为许多人不喜欢 build_runner
。
但是,一旦 Dart 中提供了静态元编程,
build_runner
将不再是问题。
届时,代码生成语法将是 Riverpod 中唯一可用的语法。
如果使用 build_runner
对您来说是一个破坏性的事情,
那么只有那时您才应该考虑不使用代码生成。
但请记住,您将错过一些功能,并且将来您将不得不迁移到代码生成。
尽管如此,当这种情况发生时,
Riverpod 将提供一个迁移工具,以使过渡尽可能顺利。
使用代码生成有什么好处?
您可能想知道:“如果 Riverpod 中代码生成是可选的,为什么要使用它?”
这和其他包的目的一样:让您的生活更轻松。这包括但不限于:
- 更好的语法,更具可读性/灵活性,并且学习曲线更短。
- 无需担心提供者程序的类型。写下您的逻辑,Riverpod 将为您选择最合适的提供者程序。
- 语法看起来不再像我们定义了“肮脏的全局变量”。相反,我们定义了一个自定义函数/类。
- 向提供者程序传递参数现在不受限制。
您现在可以传递任何参数,而不是仅限于使用
.family
并传递单个位置参数。 这包括命名参数、可选参数,甚至默认值。
- 用 Riverpod 编写的代码的有状态热重载。
- 通过生成调试器随后拾取的额外元数据来更好地进行调试。
- 某些 Riverpod 功能仅在代码生成时可用。
语法
定义提供者程序:
使用代码生成定义提供者程序时,记住以下几点会很有帮助:
- 提供者程序可以定义为带注释的函数或
带注释的类。它们几乎相同,
但基于类的提供者程序的优点是包含公共方法,使
外部对象能够修改提供者程序的状态(副作用)。
函数提供者程序是用于编写基于类的提供者程序的语法糖,只有
build
方法, 因此不能由 UI 修改。 - 支持所有 Dart 异步原语(Future、FutureOr 和 Stream)。
- 当函数被标记为async时, 提供者程序会自动处理错误/加载状态并公开 AsyncValue。
函数式的 (不能使用公共方法执行副作用) | 基于类的 (可以使用公共方法执行副作用) | |
---|---|---|
同步的 |
|
|
异步的 - Future |
|
|
异步的 - Stream |
|
|
启用/禁用自动处置 autoDispose:
使用代码生成时,提供者程序默认为 autoDispose。
这意味着当没有监听器附加到它们(ref.watch/ref.listen)时,它们会自动处理掉自己。
此默认设置更符合 Riverpod 的理念。
最初没有使用代码生成变体时,默认情况下 autoDispose 处于关闭状态,
以适应从 package:provider
迁移的用户。
如果您想禁用 autoDispose,可以通过将 keepAlive: true
传递给注释来实现。
// AutoDispose provider (keepAlive is false by default)
String example1(Ref ref) => 'foo';
// Non autoDispose provider
(keepAlive: true)
String example2(Ref ref) => 'foo';
将参数传递给提供者程序(family):
使用代码生成时,我们不再需要依赖 family
修饰符将参数传递给提供者程序。
相反,我们的提供者程序的主函数可以接受任意数量的参数,包括命名、可选或默认值。
但请注意,这些参数应该仍然具有 == 的一致性。
这意味着要么应该缓存值,要么应该覆盖 == 参数。
函数式的 | 基于类的 |
---|---|
|
|
从非代码生成变体迁移:
使用非代码生成变体时,需要手动确定提供者程序的类型。 以下是转换为代码生成变体的相应选项:
Provider | |
之前 |
|
之后 |
|
NotifierProvider | |
之前 |
|
之后 |
|
FutureProvider | |
之前 |
|
之后 |
|
StreamProvider | |
之前 |
|
之后 |
|
AsyncNotifierProvider | |
之前 |
|
之后 |
|
StreamNotifierProvider | |
之前 |
|
之后 |
|