blazor webapp预呈现导致页面初始化事件复杂化
blazor webapp预呈现导致页面初始化事件复杂化
blazor server的页面初始化事件只触发一次,blazor webapp的页面初始化事件会触发两次,如果在页面初始化事件中执行繁忙操作,需要考虑规避多次调用的问题。
回顾blazor server的页面初始化事件
VS2022新建一个blazor server项目,就用模板代码,把项目框架改为.net 8。
WeatherForecastService.cs获取天气预报功能增加延时返回,模拟繁忙任务。
public async Task<WeatherForecast[]> GetForecastAsync(DateOnly startDate) { Debug.WriteLine($"{DateTimeOffset.Now:G}, 开始调用繁忙任务"); await Task.Delay(3000); Debug.WriteLine($"{DateTimeOffset.Now:G}, 完成调用繁忙任务"); return Enumerable.Range(1, 5).Select(index => new WeatherForecast { Date = startDate.AddDays(index), TemperatureC = Random.Shared.Next(-20, 55), Summary = Summaries[Random.Shared.Next(Summaries.Length)] }).ToArray(); }
FetchData.razor天气预报页面增加打印调试信息。
protected override async Task OnInitializedAsync() { Debug.WriteLine($"{DateTimeOffset.Now:G}, OnInitializedAsync"); forecasts = await ForecastService.GetForecastAsync(DateOnly.FromDateTime(DateTime.Now)); } protected override async Task OnAfterRenderAsync(bool firstRender) { Debug.WriteLine($"{DateTimeOffset.Now:G}, OnAfterRenderAsync, firstRender={firstRender}"); }
调试运行,只触发一次页面初始化事件。
2025/5/5 9:49:03, OnInitializedAsync
2025/5/5 9:49:03, 开始调用繁忙任务
2025/5/5 9:49:03, OnAfterRenderAsync, firstRender=True
2025/5/5 9:49:06, 完成调用繁忙任务
2025/5/5 9:49:06, OnAfterRenderAsync, firstRender=False
OnInitializedAsync开始调用繁忙任务,紧接着触发OnAfterRenderAsync,此时是第一次render。
OnInitializedAsync调用繁忙任务结束后,会自动触发一次OnAfterRenderAsync,此时已经不是第一次render。
blazor webapp服务器和客户端呈现模式的页面初始化事件
新建blazor webapp项目,设为服务器和客户端呈现,每个页面单独呈现。修改模板代码,从ForecastService模块读取天气预报。
Weather.razor天气预报页面设为服务器呈现,参考blazor server项目增加打印调试信息。
@attribute [StreamRendering]
@rendermode InteractiveServer
调试运行,触发了两次页面初始化事件,调用了两次繁忙任务。
2025/5/5 11:01:52, OnInitializedAsync
2025/5/5 11:01:52, 开始调用繁忙任务
2025/5/5 11:01:55, 完成调用繁忙任务
2025/5/5 11:01:55, OnInitializedAsync
2025/5/5 11:01:55, 开始调用繁忙任务
2025/5/5 11:01:55, OnAfterRenderAsync, firstRender=True
2025/5/5 11:01:58, 完成调用繁忙任务
2025/5/5 11:01:58, OnAfterRenderAsync, firstRender=False
微软官网对预呈现模式的介绍
Blazor webapp增加了预呈现模式,页面初始化事件行为发生了改变,微软官网有相关的介绍。
https://learn.microsoft.com/zh-cn/aspnet/core/blazor/components/lifecycle?view=aspnetcore-8.0#stateful-reconnection-after-prerendering
在服务器上预呈现时,组件最初作为页面的一部分静态呈现。 浏览器重新建立与服务器的 SignalR 连接后,将再次呈现组件,并且该组件为交互式。 如果存在用于初始化组件的 OnInitialized{Async} 生命周期方法,则该方法执行两次:
l 在静态预呈现组件时执行一次。
l 在建立服务器连接后执行一次。
在最终呈现组件时,这可能导致 UI 中显示的数据发生明显变化。 若要避免此行为,请传递一个标识符以在预呈现期间缓存状态并在预呈现后检索状态。
【示例代码用MemoryCache缓存数据】
https://learn.microsoft.com/zh-cn/aspnet/core/blazor/components/prerender?view=aspnetcore-8.0#persist-prerendered-state
为了在预呈现期间保留计数器的初始值,Blazor 支持使用 PersistentComponentState 服务在预呈现页面中保留状态(对于嵌入到 Razor Pages 或 MVC 应用的页面或视图中的组件,使用持久组件状态标记帮助程序)。
若要保留预呈现状态,请决定使用 PersistentComponentState 服务保留什么状态。 PersistentComponentState.RegisterOnPersisting 注册回调以在预呈现期间保留组件状态。 当组件以交互方式呈现时,将检索状态。 在初始化代码结束时进行调用,以避免在应用关闭期间出现潜在的争用条件。
【示例代码用PersistentComponentState缓存数据】
blazor webapp服务器呈现模式的页面初始化事件
新建blazor webapp项目,设为服务器呈现,所有页面统一呈现,这种设置最接近blazor server。
App.razor
<Routes @rendermode="InteractiveServer" />
修改Weather.razor获取天气预报代码,从ForecastService模块读取天气预报,参考blazor server项目增加打印调试信息。
调试运行,只触发一次页面初始化事件,跟blazor server完全一样。
结论
对于小型的简单项目,可以使用blazor webapp项目框架,设置为服务器呈现,全局统一,只触发一次页面初始化事件,跟blazor server一样。
假如项目有一些页面需要在客户端呈现,要注意处理两次页面初始化事件的问题,可以参考微软官网的方案。
浙公网安备 33010602011771号