我目前正在进行一个使用Blazor WebAssembly作为前端框架的项目,然而,我在状态管理方面遇到了一些复杂问题,我认为。这个问题发生在一个大型复杂的解决方案中,所以我重新创建并简化了它,这样我就可以在这里简洁地描述它,而不会出现所有膨胀。
我有一个包含两个组件的页面:朋友列表和关注者列表,每个组件都有一个按钮,可以按升序或降序对列表进行排序。
如果我覆盖并在FollowerList组件内部的OnParametersSetAsync上设置断点,并单击FriendListollowerList组件上达到我设置的断点。
我的印象是,只有当提供给组件的属性发生更改或其状态发生更改时,才会对该组件调用OnParameterSet/Async。在我下面的代码中,你可以看到我有两个单独的列表,一个被馈送到一个组件,一个馈送到另一个,所以我不确定这个方法是如何在没有被触摸的组件上触发的。。。
因此,我的问题是:为什么它会触及无关组件的断点?这是故意的行为吗?
页面视图
索引页面,包含两个列表
指数剃刀
@page "/"
<button value="Sort Friends" @onclick=SortFriends>Sort Friends</button>
<FriendList Friends=SortedFriends />
<button value="Sort Followers" @onclick=SortFollowers>Sort Followers</button>
<FollowerList Followers=SortedFollowers />
Index.cs(代码隐藏(
using StateManagementTest.Model;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace StateManagementTest.Pages
{
public partial class Index
{
public List<Friend> MyFriends { get; set; }
public List<Friend> SortedFriends { get; set; }
public List<Follower> MyFollowers { get; set; }
public List<Follower> SortedFollowers { get; set; }
private bool IsFriendsDescending { get; set; }
private bool IsFollowersDescending { get; set; }
protected override async Task OnInitializedAsync()
{
MyFriends = new List<Friend>()
{
new Friend(){ FirstName = "Jack", LastName = "Sparrow" },
new Friend(){ FirstName = "Davey", LastName = "Jones"},
new Friend(){ FirstName = "Iron", LastName = "Man"}
};
MyFollowers = new List<Follower>()
{
new Follower(){ Username = "user1234" },
new Follower(){ Username = "abc123" },
new Follower(){ Username = "david123"}
};
SortedFriends = MyFriends;
SortedFollowers = MyFollowers;
}
private void SortFriends()
{
if (!IsFriendsDescending)
{
SortedFriends = MyFriends.OrderByDescending(f => f.FirstName).ToList();
IsFriendsDescending = true;
return;
}
SortedFriends = MyFriends.OrderBy(f => f.FirstName).ToList();
IsFriendsDescending = false;
}
private void SortFollowers()
{
if (!IsFollowersDescending)
{
SortedFollowers = MyFollowers.OrderByDescending(f => f.Username).ToList();
IsFollowersDescending = true;
return;
}
SortedFollowers = MyFollowers.OrderBy(f => f.Username).ToList();
IsFollowersDescending = false;
}
}
}
FriendList.rarzor
<h1>Friends</h1>
@foreach (var friend in Friends)
{
<div>@friend.FirstName @friend.LastName</div>
}
FriendList.cs(代码隐藏(
using Microsoft.AspNetCore.Components;
using StateManagementTest.Model;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace StateManagementTest.Components
{
public partial class FriendList
{
[Parameter]
public List<Friend> Friends { get; set; }
}
}
追随者列表.rarzor
<h1>Followers</h1>
@foreach (var follower in Followers)
{
<div>@follower.Username</div>
}
FollowerList.cs(代码隐藏(
using Microsoft.AspNetCore.Components;
using StateManagementTest.Model;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace StateManagementTest.Components
{
public partial class FollowerList
{
[Parameter]
public List<Follower> Followers { get; set; }
protected override Task OnParametersSetAsync()
{
return base.OnParametersSetAsync();
}
}
}
经过更多的研究,我的同事也深入研究了这个问题,我们发现了导致这种行为的原因。
我开始在这里再次查看组件生命周期链接,并强调了这部分文本:
OnParametersSetAsync或OnParametersSet被调用:
- 在OnInitialized或OnInitializedSync中初始化组件之后
- 当父组件重新渲染并提供:
- 只有已知的原语不可变类型,其中至少有一个参数已更改
- 任何复杂类型的参数。框架无法知道复杂类型参数的值是否在内部发生了变化,因此它将参数集视为已更改
因此,这将我的问题改为:是什么导致父组件重新渲染?本能地,我开始观察按钮在Blazor中的行为,并快速阅读用于Blazor事件处理的microsoft文档,即在每个UI事件上调用StateHasChanged
,无论是onclick
、onchange
等等。因此,当我单击按钮时,它也在调用StateHasChanged
,这导致组件重新呈现并重新分配后续的子级参数,只要它们是复杂类型。