乐于分享
好东西不私藏

为什么你用不好 Riverpod?我把源码翻了一遍,发现问题不在 API

为什么你用不好 Riverpod?我把源码翻了一遍,发现问题不在 API

很多人学 Riverpod 的时候,都会经历一个阶段:

会用,但总感觉哪里不对

比如:

  • 写法越来越复杂
  • Provider 越来越多
  • 依赖关系越来越乱

最后变成:

能跑,但不好维护


我最近把 Riverpod 的源码重新过了一遍,顺带看了一些静态分析规则(DCM rules),有一个挺深的感受:

很多问题,不是你不会用,而是你用错了“方式”。

这篇我不讲 API,而是讲几个源码层面的设计思想,以及它们在实际项目里的影响。


一、Riverpod 最核心的东西,其实不是 Provider

很多人一提 Riverpod,想到的是:

Provider / StateNotifier / FutureProvider

但源码里真正核心的是:

ProviderContainer


它本质是什么?

可以理解为:

状态运行时(State Runtime)

所有 Provider:

  • 创建
  • 缓存
  • 销毁
  • 依赖关系

全都在这个容器里完成


为什么这很重要?

因为这意味着:

Provider 不是全局单例,而是“容器内的实例”


一个典型误区

很多人会这样用:

final counterProvider = StateProvider((ref) => 0);

然后默认它是“全局唯一”。

但实际上:

不同 ProviderContainer → 不同实例

这也是为什么 Riverpod 可以做到:

  • 测试隔离
  • 多环境运行
  • 覆盖(override)

二、依赖追踪才是 Riverpod 的“灵魂”

你写的这句代码:

final value = ref.watch(otherProvider);

看起来很简单,但背后做了很多事:


发生了什么?

1. 记录依赖关系
2. 建立订阅
3. 当依赖变化 → 触发重新计算

关键点

依赖是“自动收集”的,而不是你手动管理的


但问题也出在这里

很多人会写出这种代码:

ref.watch(a);
ref.watch(b);
ref.watch(c);

然后:

一个变化 → 全部重新执行


源码层面的结论是:

watch 越多,重算范围越大


三、为什么会出现“性能问题”

很多人觉得 Riverpod 慢,其实不是它慢,而是你这样写了:


典型错误写法

final complexProvider = Provider((ref) {
final a = ref.watch(aProvider);
final b = ref.watch(bProvider);
final c = ref.watch(cProvider);

return heavyCompute(a, b, c);
});

问题:

任意一个变化 → heavyCompute 全执行

源码角度看,本质是:

Provider 是“计算单元”,不是“字段容器”


四、为什么官方一直推荐拆 Provider

很多人不理解为什么要拆:

不是一个 Provider 搞定更简单吗?

但源码的设计是:

Provider 越小,更新越精确


正确思路

final aProvider = ...
final bProvider = ...
final cProvider = ...

final resultProvider = Provider((ref) {
final a = ref.watch(aProvider);
final b = ref.watch(bProvider);

return computeLight(a, b);
});

把“重计算”和“数据源”分开


五、autoDispose 不是性能优化工具

很多人会这样理解:

用 autoDispose = 更省资源

但源码逻辑其实是:

无监听 → 销毁

它解决的是:

生命周期问题

而不是:

性能问题

滥用后果

频繁创建 / 销毁 → 反而更慢

六、DCM 规则在约束什么

这部分很多人没注意,但其实很有价值。

DCM(静态分析规则)在干嘛?

限制你写出“违背设计初衷”的代码


比如:

1 避免在 Provider 里做副作用
不要在 build 里写请求 / 写入

2 避免滥用 ref.read
read → 不建立依赖
watch → 建立依赖

用错会导致:

  • UI 不更新
  • 状态不同步

3 控制依赖数量
watch 太多 → 重建范围扩大

七、一个更本质的理解

我看完源码之后,对 Riverpod 的理解变成了一句话:

它不是状态管理工具,而是“依赖计算系统”


它更像什么?

Spreadsheet(表格)

比如:

A1 = 1
B1 = A1 + 1

当 A1 变:

B1 自动更新

Riverpod 本质就是在做这个


八、为什么很多人用不好

不是因为难,而是因为:

你用“状态管理思维”在用“计算系统”


常见问题本质

问题
根因
rebuild 太多
依赖太粗
状态混乱
依赖关系不清
难维护
Provider 过大

九、我给你的实用建议

不讲虚的,直接能用的:


1 Provider 尽量小

一个 Provider 做一件事

2 watch 要克制

只 watch 必要依赖

3 计算拆分

重计算不要绑在大 Provider 上

4 区分 read / watch

UI → watch
逻辑 → read

最后一句

很多人觉得 Riverpod 难,是因为它不像传统状态管理。

但如果你换个角度看:

它其实是在帮你管理“依赖关系”,而不是“状态本身”

一旦理解这一点,你写出来的代码,会干净很多。