为什么你用不好 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 本质就是在做这个
八、为什么很多人用不好
不是因为难,而是因为:
你用“状态管理思维”在用“计算系统”
常见问题本质
|
|
|
|---|---|
|
|
|
|
|
|
|
|
|
九、我给你的实用建议
不讲虚的,直接能用的:
1 Provider 尽量小
一个 Provider 做一件事
2 watch 要克制
只 watch 必要依赖
3 计算拆分
重计算不要绑在大 Provider 上
4 区分 read / watch
UI → watch
逻辑 → read
最后一句
很多人觉得 Riverpod 难,是因为它不像传统状态管理。
但如果你换个角度看:
它其实是在帮你管理“依赖关系”,而不是“状态本身”
一旦理解这一点,你写出来的代码,会干净很多。
夜雨聆风