Blazor中如何强制仅含不可变参数的组件重渲染?
在基于.NET 5的Blazor项目中,父组件通过RenderFragment包含子组件,子组件继承自ChildComponentBase。其中ChildComponent1仅包含不可变的string类型参数Title,ChildComponent2额外包含可变枚举参数。父组件采用延迟渲染策略,通过Defer组件将渲染移至队列末尾以收集子组件:首次渲染时,子组件通过级联参数将自身添加到父组件集合,随后调用ProcessDataAsync触发父组件重渲染。但第二次重渲染时,ChildComponent1因参数全为不可变类型,被Blazor变更检测API跳过重渲染,导致父组件无法再次收集到它。以下是几种无需缓存子集合的优雅解决方案:
方案1:添加强制渲染触发参数
在ChildComponentBase中新增一个可变参数,父组件每次重渲染时更新该参数值,让Blazor检测到参数变化从而触发子组件渲染。
修改ChildComponentBase:
public partial class ChildComponentBase { [CascadingParameter] public ParentComponent Parent { get; set; } = default!; [Parameter] public string? Title { get; set; } // 不可变参数 [Parameter] public SampleEnum SampleEnum { get; set; } // 可变参数 // 新增强制渲染触发参数 [Parameter] public object? RenderTrigger { get; set; } }
修改父组件逻辑,维护触发值并在重渲染时更新:
public partial class ParentComponent { private List<ChildComponentBase> _childComponents = new(); private bool _firstRender = true; private object _renderTrigger = new(); // 触发渲染的标记对象 protected async Task ProcessDataAsync() { if (_firstRender) { await InvokeAsync(StateHasChanged); _firstRender = false; } else { // 更新触发值,强制子组件检测到参数变化 _renderTrigger = new(); await InvokeAsync(StateHasChanged); } } }
在父组件使用子组件时绑定触发参数:
<ParentComponent> <ChildComponent1 Title="Component1" RenderTrigger="@_renderTrigger"></ChildComponent1> <ChildComponent2 Title="Component2" SampleEnum="SampleEnum.Bar" RenderTrigger="@_renderTrigger"></ChildComponent2> </ParentComponent>
方案2:重写子组件ShouldRender方法
让ChildComponent1或ChildComponentBase重写ShouldRender方法,直接返回true,强制每次执行渲染逻辑。此方式简单直接,若子组件渲染逻辑较轻则无性能顾虑。
修改ChildComponent1:
public class ChildComponent1 : ChildComponentBase { protected override bool ShouldRender() { // 强制每次都渲染 return true; } }
方案3:利用级联参数变化触发渲染
在父组件中新增一个级联参数,每次重渲染时更新其值,子组件接收该级联参数,Blazor会因级联参数变化触发子组件渲染。
修改父组件:
public partial class ParentComponent { private List<ChildComponentBase> _childComponents = new(); private bool _firstRender = true; private int _renderCounter = 0; // 触发级联参数变化的计数器 // 注册级联参数 [CascadingParameter(Name = "RenderCounter")] public int RenderCounter { get; set; } protected async Task ProcessDataAsync() { if (_firstRender) { await InvokeAsync(StateHasChanged); _firstRender = false; } else { _renderCounter++; await InvokeAsync(StateHasChanged); } } }
修改ChildComponentBase接收级联参数:
public partial class ChildComponentBase { [CascadingParameter] public ParentComponent Parent { get; set; } = default!; [Parameter] public string? Title { get; set; } [Parameter] public SampleEnum SampleEnum { get; set; } // 接收父组件的级联计数器 [CascadingParameter(Name = "RenderCounter")] public int RenderCounter { get; set; } }
每次父组件更新_renderCounter,子组件的RenderCounter参数会同步变化,Blazor将自动触发子组件重渲染,确保父组件能再次收集到ChildComponent1。
内容的提问来源于stack exchange,提问作者Aleksandr Stepanov

