Mvvm 为每个Blazor回路创建一次对象(ViewModel)
我目前正在编写一个小型Blazor Web UI。我正在尝试采用/实现MVVM模式。为此,我创建了两个组件基类。一个只处理Blazor生命周期方法(添加一些异常处理),另一个根据这些生命周期方法的执行处理ViewModel初始化。该组件还实现IDisposable接口,当组件不可见时,Blazor会自动调用该接口 下面是我的WebComponentBase类和ViewModelAwareComponent类的代码片段,大致介绍一下该模式:Mvvm 为每个Blazor回路创建一次对象(ViewModel),mvvm,dependency-injection,viewmodel,blazor,circuit,Mvvm,Dependency Injection,Viewmodel,Blazor,Circuit,我目前正在编写一个小型Blazor Web UI。我正在尝试采用/实现MVVM模式。为此,我创建了两个组件基类。一个只处理Blazor生命周期方法(添加一些异常处理),另一个根据这些生命周期方法的执行处理ViewModel初始化。该组件还实现IDisposable接口,当组件不可见时,Blazor会自动调用该接口 下面是我的WebComponentBase类和ViewModelAwareComponent类的代码片段,大致介绍一下该模式: 公共抽象类WebFpComponentBase:Com
公共抽象类WebFpComponentBase:ComponentBase、IDisposable、IHtmlStyles
{
private const string DEFAULT\u DIM\u VALUE=“auto”;
[注入]
受保护的IEExceptionUIHandler异常处理程序{get;set;}
//为简洁起见,省略了字段和参数
#区域Blazor组件生命周期
///
///当组件接收到所有初始参数时,在OnInitialized之后调用OnInitialized。将所有异步操作放在此处,
///这需要组件重新渲染。
///
///
受保护的重写异步任务OnInitializedAsync()
{
尝试
{
wait base.OnInitializedAsync();
_Info($“{nameof(OnInitializedAsync)}-在${this.GetType().FullName}类型的组件中调用的方法”);
等待OnInitializedInternalAsync();
_logger.Info($“{nameof(OnInitializedAsync)}-方法在${this.GetType().FullName}类型的组件中完成);
}捕获(例外情况除外)
{
//如果发生异常,则使用AfterRenderAsync()方法中的IEExceptionUIHandler阻止异常
_forwardException=ex;
_logger.Error($“{nameof(OnInitializedAsync)}:捕获和转发类型为{u forwardException.GetType().FullName}的异常”;
}
}
受保护的抽象任务OnInitializedInternalAsync();
//为简洁起见,省略了该类的其他方法
}
接下来是我的ViewModelAwareComponent,它确实有一个包含ViewModel的属性,并通过实现[BlazorLifecycleMethod]内部抽象方法自动启动ViewModel初始化和反初始化(关闭任何服务连接、重置任何值等)
公共抽象类ViewModelAwareComponent:WebFpComponentBase其中TViewModel:BaseViewModel
{
专用记录器_Logger=LogManager.GetCurrentClassLogger();
[参数]
公共虚拟TViewModel ViewModel{get;set;}
受保护的重写异步任务OnInitializedInternalAsync()
{
等待ViewModel.InitializeAsync();
ViewModel.PropertyChanged+=this.FireComponentState已更改;
}
公共重写异步void Dispose()
{
base.Dispose();
等待ViewModel.DeactivateAsync();
}
受保护的虚拟异步void FireComponentState已更改(对象发送方,PropertyChangedEventArgs e)
{
_Trace($“FireComponentStateShasChanged:属性{e.PropertyName}已更改!”);
等待InvokeAsync(this.StateHasChanged);
}
受保护的重写异步任务OnParametersSetAsyncInternal()
{
if(ViewModel==null)
{
抛出新ArgumentNullException($“{nameof(ViewModel)}”,“必须提供参数!”);
}
}
}
BaseViewModel类型仅实现以典型方式更改的InotifyProperty。我有一个“MainViewModel”,在整个连接(Blazor电路)中只应实例化一次。因此,我通过Startup.cs
中的services.addScope()
添加了它。因为它没有绑定到任何特定组件,所以我将它注入到我的MainLayout.razor
中,这是我编写的每个razor组件的布局
布局在InitializedAsync()上实现了受保护的覆盖异步任务,如下所示:
受保护的重写异步任务OnInitializedAsync()
{
MainViewModel.PropertyChanged+=(obj,args)=>InvokeAsync(StateHasChanged);
等待MainViewModel.InitializeAsync();
//后来发生了一些其他事情
}
我现在的问题是,每次启动应用程序时都会进行两次初始化,而不是每次连接一次。去初始化也是如此,因为组件的Dispose()也被调用了两次。
调试时,我注意到在两个现有页面(路由组件)之间切换时,不会重新调用OnInitializedAsync
。启动时只调用两次
你对这种行为有什么建议吗?是否有更好的方法来实现MainViewModel的预期行为
致以最良好的祝愿
tilt由dani herrera的评论回答:
也许是关于预渲染?请看我的回答:
通过更改属性禁用预渲染
@(等待Html.RenderComponentAsync(RenderMode.ServerPrerendered))
到
@(等待Html.RenderComponentAsync(RenderMode.Server))
现在MainLayout.razor的OnInitializedAsync()只被调用了一次。我看到这一点得到了回答,但我们也通过编写一个小型Blazor回路处理程序,然后将返回的回路id映射到一个作用域会话服务来实现这一点。创建(或断开/重新连接)新回路时,回路处理程序提供事件
为什么要调用wait MainViewModel.InitializeAsync()代码>在布局中OnInitializedAsync
?可能是关于预渲染的?请看我的回答:@aguafrommars MainViewModel确实拥有WCF服务连接,必须先建立连接,然后应用程序才能启动。作为V