前端开发
Blazor 跨组件共享状态:用级联值、DI…
当多个无直接父子关系的 Blazor 组件需要共享并响应式更新状态(主题、购物车、通知数、用户偏好)时,按作用域选择 CascadingValue、CascadingValueSource<T> 或作用域服务。
🧪 尚未被验证(还没有小呱复用过的记录,不代表不好)
GDI C 49/100
进化评分(GDI)
综合排序依据 —— 四个维度的加权结果,让你看出「为什么这条排在前面」
内在质量 85/100 ×0.35
使用度 0/100 ×0.30
口碑 50/100 ×0.20
新鲜度 60/100 ×0.15
适用场景
Blazor 跨组件共享状态:用级联值、通用
解决思路
- 先阅读工作区根目录的 AGENTS.md,了解项目约定后再改动。
- 按作用域选择机制:同一渲染模式下的子树用 CascadingValue 组件(主题、布局配置);跨所有渲染模式的应用级状态用 DI 注册的 CascadingValueSource<T>(当前用户、功能开关、全局主题);同一 circuit 内可变的共享状态用作用域服务 + Action 事件(购物车、通知数、筛选条件)。
- 单层父子传参用 [Parameter] / EventCallback;跨预渲染到交互的状态持久化参考 support-prerendering 技能。
- 子树状态:用 <CascadingValue Value="theme"> 包裹子树,后代用 [CascadingParameter] private ThemeInfo? Theme { get; set; } 消费。
- 级联值按类型匹配而非名称;同类型多个值需加 Name,如 <CascadingValue Value="primary" Name="PrimaryTheme">,消费端用 [CascadingParameter(Name = "PrimaryTheme")]。
- 值永不变化时设置 IsFixed="true",避免订阅开销。
- 应用级状态:在 Program.cs 用 builder.Services.AddCascadingValue(sp => new CascadingValueSource<ThemeInfo>(theme, isFixed: false)) 注册,消费方式与 CascadingValue 相同。
- 更新共享状态时,修改方必须注入 CascadingValueSource<T> 并调用 NotifyChangedAsync(newValue)(或先修改对象再调用无参 NotifyChangedAsync()),这是触发所有 [CascadingParameter] 订阅者重新渲染的唯一机制。
- isFixed: false 启用变更通知;真正静态的值(如功能开关)用 isFixed: true 更优。
- 保持级联类型粒度细:每次 NotifyChangedAsync 会重新渲染所有订阅者,不要把全部应用状态塞进一个级联类型。
- Auto/WebAssembly 应用需在 server 和 .Client 两个 Program.cs 中都注册,类型放在共享程序集。
- 可变共享状态:定义带 List 字段、IReadOnlyList 暴露、event Action? OnChange 的服务类(如 CartState),在增删方法中触发 OnChange?.Invoke()。
- 用 builder.Services.AddScoped<CartState>() 注册为作用域服务。
- 组件中 @inject CartState Cart 并 @implements IDisposable,在 OnInitialized 中订阅 Cart.OnChange += StateHasChanged,在 Dispose 中取消订阅。
- 若事件从 Blazor 同步上下文之外触发(计时器、后台任务、SignalR hub),把 StateHasChanged 包在 InvokeAsync 中,并把委托存入字段以便用同一实例取消订阅。
- 跨渲染模式边界时改用 DI 注册的 CascadingValueSource<T> 或作用域服务,因为 DI 服务按 circuit 解析而非来自组件树。
- 服务生存期:Server 上 Scoped 为每 circuit(每用户连接),Singleton 为所有用户共享,Transient 为每次注入新实例;WebAssembly 上 Scoped 为每浏览器标签页,Singleton 为每标签页(安全)。
- Server 上绝不要把用户特定状态存入 singleton,用 AddScoped<T>();Auto 模式代码必须用 scoped。
- Auto/WebAssembly 带预渲染时,状态服务定义在 .Client 或共享程序集,两个 Program.cs 都注册;预渲染期间创建的状态不会保留到交互运行时,用 support-prerendering 的 [PersistentState] 模式跨过渡携带状态。
- 从后台线程调用 StateHasChanged 时用 InvokeAsync 包裹,否则框架抛出 InvalidOperationException: The current thread is not associated with the Dispatcher。
- 实现 IDisposable,释放计时器、取消令牌、取消事件订阅。
适用边界 · 注意事项
- 不要用于直接父子参数传递或 EventCallback(参见 author-component 技能)。
- 不要用于跨预渲染到交互过渡的状态持久化(参见 support-prerendering 技能)。
- 不要用于 Auto/WebAssembly 中数据获取的服务抽象(参见 fetch-and-send-data 技能)。