[译][官方文档] Flutter/Dart 状态管理库 Riverpod - 高级话题 - 性能优化

172 阅读2分钟

原文链接:Optimizing performance | Riverpod

pub:riverpod | Dart Package (flutter-io.cn)

译时版本: 2.4.9


之前翻译过 Riverpod 的官方文档,现在随着版本更新,官方文档又多了很多新内容,所以再补充翻译一下。
之前翻译过的内容,现在官方文档有中文了。
Flutter状态管理库Riverpod官方文档翻译汇总 - 掘金 (juejin.cn)


改善性能

用至今为止前面看到的所有内容,已经可以构建一个完整的功能性应用了。 尽管如此,可能还是会有一些性能考量方面的问题。

本篇文章会提供一些提示和技巧来尽可能地改善代码。

警告

在进行改善之前,先确认下应用的性能指标。
改善性能产生的复杂性可能得不偿失。

使用 "select" 过滤组件/provider 的重建

你可能已经注意到了,使用 ref.watch 默认会在对象的任意属性改变时导致 consumer/provider 重新构建。
例如,监听一个 User (用户)但是只使用它的"name"(名字)也会在"age"(年龄)改变时导致 consumer(消费者)重新构建。

但是假设有一个使用属性子集的 consumer(消费者),想在其它属性改变时避免重新构建组件。

可使用 provider 的 select 功能来实现。
这样做时,ref.watch 不再返回整个对象,而是返回被选择的属性。
这样 consumers/provider 现在只会在这些被选择的属性改变时进行重新构建。

class User {
  late String firstName, lastName;
}

@riverpod
User example(ExampleRef ref) => User()
  ..firstName = 'John'
  ..lastName = 'Doe';

class ConsumerExample extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    // 代替以下代码:
    // String name = ref.watch(provider).firstName!;
    // 可以写为:
    String name = ref.watch(exampleProvider.select((it) => it.firstName));
    // 这会使组件只监听 "firstName" 的改变。

    return Text('Hello $name');
  }
}

信息

select 想调用多少次就调用多少次。只要你想,每个属性调用各调用一次都可以。

警告

被选择的属性预期是不可变的。 返回 List 并修改它们不会触发重新构建。

警告

使用 select 会略微降低单个读操作的速度,并且会增加一些代码的复杂度。
如果 “其它属性” 很少发生改变,那可能不值得这么做。

选择异步属性

假设在尝试优化一个正在监听其它 provider 的 provider ,那其它 provider 很可能是异步的。

正常情况下,会使用 ref.watch(anotherProvider.future) 来获取值。

问题是,select 只会用于 AsyncValue – 这个是不能 await (等待)的。

为了该达到该目的,可以使用 selectAsync 代替。对于异步代码这是唯一的,然后要使 provider 产出的数据能进行 select 操作。
它的用法和 select 类似,不同的是会返回 Future

@riverpod
Object? example(ExampleRef ref) async {
  // 等待 user 可用,然后只监听 "firstName" 属性
  final firstName = await ref.watch(
    userProvider.selectAsync((it) => it.firstName),
  );

  // TODO 使用 "firstName" 来获取其它信息
}