Reference 和 Service 该用哪个?
Context.Reference 的读取不会让类型里的 R 多出依赖,这意味着忘记 provide 的时候没有任何报错,代码静默地使用默认值。在租户隔离、权限这类场景,这是事故源头。
判断标准是一句话:缺失时有没有一个安全的默认值。有(日志级别、开关关闭、追踪开启),用 Context.Reference;没有(数据库连接、当前用户、必须存在的凭证),用 Context.Service。
Context.Reference 是带默认值的服务键,v4 里取代了 v3 的 FiberRef,细节见 Effect v4 里 FiberRef 去哪了?;Context.Service 则把依赖写进类型,调用方必须提供,见 Effect 里怎么用 Context.Service 把依赖写进类型?。下面讲这条标准在具体场景里怎么落。
为什么「当前用户」不适合做 Reference?
租户和语言是边界上的合适例子:它们是横切的、有合理默认值的环境信息。而「当前登录用户」没有合理默认值,缺了它业务代码应该编译失败,而不是悄悄以匿名运行。用 Context.Service,类型系统会强迫调用方提供;用 Reference,漏掉的后果是默认值生效。
默认值要怎么选?
默认值我一律选「安全」的那一侧:feature flag 默认全部关闭,这样漏提供时是安全的降级,而不是意外上线新流程。写法上,defaultValue 是函数,只求值一次并缓存,如果返回可变对象,所有没覆盖的读取方共享同一个实例,所以默认值要么不可变,要么你明确想要全局共享。
用了 Reference 之后还要注意什么?
- 值的类型尽量只读、不可变,包括默认值。覆盖是整体替换,不是合并;需要叠加的,自己封装成
updateService或辅助函数,别让调用方直接面对原始 Reference。 - 给每个 Reference 的 key 加应用前缀(如
"myapp/Flags")。key 字符串是身份,重名会互相覆盖。
Context.Service 和 Layer 怎么组合装配,见 Effect 服务与 Layer:把依赖写进类型里。
版本是 effect@4.0.5。默认值只求值一次并缓存、可变默认值被共享,在本地跑过。「key 重名会互相覆盖」和选择标准是我的判断与建议,没有单独运行验证。