为每个 Blazor 线路创建一个对象 (视图模型) 一次



我目前正在编写一个小的Blazor Web UI。我正在尝试采用/实现 MVVM 模式。为此,我创建了两个组件基类。一个仅处理 Blazor 生命周期方法(添加一些异常处理(,另一个处理基于这些生命周期方法的执行的 ViewModel 初始化。该组件还实现了 IDisposable 接口,当组件变得不可见时,Blazor 会自动调用该接口。

下面是我的 WebComponentBase 类和 ViewModelAwareComponent 类的代码片段,大致给出了有关模式的概念:


public abstract class WebFpComponentBase : ComponentBase, IDisposable, IHtmlStyles
{
private const string DEFAULT_DIM_VALUE = "auto";
[Inject]
protected IExceptionUiHandler<ErrorRedirectViewModel> ExceptionUiHandler { get; set; }
//fields and parameters omitted for brevity
#region Blazor Component LifeCycle
/// <summary>
/// OnInitialized is called after OnInitialized, when the component has received all initial parameters. Place any asynchronous operations here,
/// which require the component to re-render.
/// </summary>
/// <returns></returns>
protected override async Task OnInitializedAsync()
{
try
{
await base.OnInitializedAsync();
_logger.Info($"{nameof(OnInitializedAsync)} - method invoked in component of type ${this.GetType().FullName}");
await OnInitializedInternalAsync();
_logger.Info($"{nameof(OnInitializedAsync)} - method finished in component of type ${this.GetType().FullName}");
} catch(Exception ex)
{
//Exception, if any happend, is forwared using the IExceptionUiHandler in the AfterRenderAsync() method
_forwardException = ex;
_logger.Error($"{nameof(OnInitializedAsync)}: Catching and forwarding exception of type {_forwardException.GetType().FullName}");
}
}
protected abstract Task OnInitializedInternalAsync();
//other methods of the class omitted for brevity
}

接下来是我的 ViewModelAwareComponent,它确实有一个包含 ViewModel 的属性,并通过实现 [BlazorLifecycle Method] 内部抽象方法自动触发 ViewModel 初始化和取消初始化(关闭任何服务连接、重置任何值等(。

public abstract class ViewModelAwareComponent<TViewModel> : WebFpComponentBase where TViewModel : BaseViewModel
{
private Logger _logger = LogManager.GetCurrentClassLogger();
[Parameter]
public virtual TViewModel ViewModel { get; set; }
protected override async Task OnInitializedInternalAsync()
{
await ViewModel.InitializeAsync();
ViewModel.PropertyChanged += this.FireComponentStateHasChanged;
}
public override async void Dispose()
{
base.Dispose();
await ViewModel.DeactivateAsync();
}
protected virtual async void FireComponentStateHasChanged(object sender, PropertyChangedEventArgs e)
{
_logger.Trace($"FireComponentStateHasChanged: property {e.PropertyName} has changed!");
await InvokeAsync(this.StateHasChanged);
}
protected override async Task OnParametersSetAsyncInternal()
{
if (ViewModel == null)
{
throw new ArgumentNullException($"{nameof(ViewModel)}", "Parameter must be supplied!");
}
}
}

BaseViewModel-Type 仅以典型方式实现 INotifyPropertyChanged。我确实有一个"MainViewModel",它应该只为整个连接(Blazor Circuit(实例化一次。因此,我通过Startup.cs中的services.AddScoped()添加了它。由于它不绑定到任何特定组件,因此我将其注入到我的MainLayout.razor这是我编写的每个 Razor 组件的布局。

布局确实实现了如下所示的protected override async Task OnInitializedAsync()

protected override async Task OnInitializedAsync()
{
MainViewModel.PropertyChanged += (obj, args) => InvokeAsync(StateHasChanged);
await MainViewModel.InitializeAsync();
//some other stuff happening afterwards
}

我现在的问题是,每次启动应用程序时都会进行两次初始化,而不是每个连接只执行一次。对于取消初始化也是如此,因为组件的 Dispose(( 也被调用了两次。

调试时,我注意到在两个现有页面(路由组件(之间切换时不会重新调用OnInitializedAsync。它仅在启动时调用两次。

你对这种行为有什么建议吗?有没有更好的方法来实现我想要的主视图模型行为?

此致敬意

倾斜

我看到这个问题得到了回答,但我们也通过编写一个小型 Blazor 电路处理程序,然后将返回的电路 ID 映射到作用域会话服务来实现这一点。线路处理程序在创建新线路(或断开连接/重新连接(时提供事件。

https://learn.microsoft.com/en-us/aspnet/core/blazor/advanced-scenarios?view=aspnetcore-5.0

由Dani Herrera的评论回答:

也许是关于预渲染?在这里看到我的答案:https://stackoverflow.com/a/57696602/842935

通过更改 @(await Html.RenderComponentAsync(RenderMode.ServerPrerendered)) @(await Html.RenderComponentAsync(RenderMode.Server))现在 MainLayout.razor 的 OnInitializedAsync(( 只调用一次。

相关内容

  • 没有找到相关文章

最新更新