我对如何优化典型数据驱动的 Blazor 组件的渲染感到困惑。考虑以下简化组件(部分):
public partial class FooComponent: ComponentBase
{
[Inject]
protected IBarService BarService { get; set; }
[Parameter]
public string OptionalParameter1 { get; set; }
[Parameter]
public string OptionalParameter2 { get; set; };
protected IEnumerable<BarItem> Items { get; set; }
protected override Task OnParametersSetAsync()
{
this.Items = await this.BarService.GetItemsAsync(this.OptionalParameter1, this.OptionalParameter2);
}
}
该组件有两个可选参数。它们通常可以是服务调用的一些过滤值。组件无法知道父组件是否会设置这些参数(因为它们是可选的)。每当设置参数时,组件都会通过(昂贵的)异步服务调用检索
BarItem
项目列表。然后组件标记以某种方式呈现 Items
列表。
问题是每次设置参数时都会调用
OnParametersSetAsync
,导致组件重新渲染并进行另一个服务调用。我可以通过检查参数值是否更改来部分优化它,但仍然会有多个服务调用。
在具有更多属性的现实组件中,这可能很快会导致许多不需要的服务调用。如果对所有参数调用一次
OnParametersSetAsync
,这不会是问题(我知道 Blazor 不会以这种方式工作)。
我可以做些什么来确保服务调用仅使用正确的参数值发生一次吗?在我看来,这似乎是数据驱动组件中非常常见的场景,如果无法(有效)实现,这将是 Blazor 的真正缺点。
昨天在 dotnet Conf Focus Blazor 上,Daniel Roth 展示了类似的东西。 要解决此问题,您可以使用每次设置其中一个参数时启动的计时器。如果计时器达到时间限制,您将调用搜索请求。 这称为去抖。
如果可以的话,我会尝试挖掘 github 存储库来进行演示。
我也遇到过这个问题,我也同意这是相当乏味的。在这里,我根据我的痛苦经历,以列表的形式提出一些反馈。希望这个对你有帮助;如果没有,请忽略它。
避免在参数中使用引用类型。如果我没记错的话(希望如此),引用类型总是会触发 OnParametersSet,而值类型仅在发生更改时才会触发。
使用可空类型,如 string?、bool?、int?这样您就可以检查是否设置了参数。
在检查参数是否已更改时,我总是使用前缀“Param”声明 [Parameter] 以及我在其下面的组件中实际使用的属性,因此我永远不会被迷惑。示例如下:
@code {
[Parameter] public string? PARAMOptionalParameter1 { get; set; }
private string? OptionalParameter1 { get; set; }
[Parameter] public string? PARAMOptionalParameter2 { get; set; }
private string? OptionalParameter2 { get; set; }
private bool _shouldRender;
private bool _shouldDownloadData;
private void FunctionToCheckForParamtersChange()
{
_shouldRender = false;
_shouldDownloadData = false;
if (PARAMOptionalParameter1 == OptionalParameter1)
{
// Do nothing or whatever you like
}
else
{
OptionalParameter1 = PARAMOptionalParameter1;
_shouldRender = true;
_shouldDownloadData = true;
}
//Other stuff and parameters, you get the idea
}
protected override bool ShouldRender()
{
return _shouldRender;
}
protected override async Task OnParametersSetAsync()
{
FunctionToCheckForParamtersChange();
if (_shouldDownloadData)
{
// DATA download
}
}
}
最简洁的方法是将所有参数分组到名为 YourComponentConfig 的类/结构中。为此类或结构实现自定义比较器,并包含一个布尔标志来指示是否设置了所有参数。因此,您可以将 FunctionToCheckForParametersChange 方法简化为一行:if (ParamConfig == Config)。
或者,您可以使用一种不太传统的方法,即创建对组件的引用并直接从父控制器修改其参数(尽管选项 5 更好)。例如:
@代码 { 成分? ComponentRef{获取;设置;}
无效SetData(){ ComponentRef?.SetMyData("Data1","Data2"); // 请记住,我们的自定义 SetMyData 方法超出了渲染循环,因此您应该放入 SetMyData InvokeAsync(StateHasChanged) 以使渲染发生。 } }