Blazor Server中如何让异步方法在OnInitialized前执行完成?
Blazor Server 全局加载LocalStorage数据并确保页面组件能获取到的解决方案
核心问题分析
Blazor Server中,App.Razor的OnInitializedAsync是异步执行的,而页面组件的初始化方法会和App的初始化并行执行,导致页面组件初始化时,App中加载LocalStorage的异步操作还没完成,拿不到目标数据。
方案一:全局延迟渲染,确保数据加载完成后再渲染页面
这种方案会在App层面等待数据加载完成,之后才渲染Router和页面组件,从根源上避免页面组件提前初始化。
- 定义Scoped数据服务
public class AppDataService { // 标记数据是否加载完成 public bool IsDataLoaded { get; private set; } // 存储从LocalStorage获取的数据 public UserPreferences Preferences { get; private set; } public async Task LoadFromProtectedLocalStorage(ProtectedLocalStorage localStorage) { var storageResult = await localStorage.GetAsync<UserPreferences>("user-preferences"); Preferences = storageResult.Success ? storageResult.Value : new UserPreferences(); // 加载完成后更新标记 IsDataLoaded = true; } }
(注:UserPreferences是你自定义的实体类,用来存储需要跨请求保留的数据)
- 修改App.Razor,添加加载状态判断
@inject AppDataService DataService @inject ProtectedLocalStorage LocalStorage @* 只有数据加载完成后才渲染路由 *@ @if (DataService.IsDataLoaded) { <Router AppAssembly="@typeof(App).Assembly"> <Found Context="routeData"> <RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" /> <FocusOnNavigate RouteData="@routeData" Selector="h1" /> </Found> <NotFound> <PageTitle>Not found</PageTitle> <LayoutView Layout="@typeof(MainLayout)"> <p role="alert">抱歉,该地址不存在。</p> </LayoutView> </NotFound> </Router> } else { <div style="display: flex; justify-content: center; align-items: center; height: 100vh;"> <p>加载中...</p> </div> } @code { protected override async Task OnInitializedAsync() { // 统一从LocalStorage加载数据到Scoped服务 await DataService.LoadFromProtectedLocalStorage(LocalStorage); } }
这样所有页面组件都会在数据加载完成后才开始初始化,直接就能拿到AppDataService中的数据。
方案二:页面组件等待数据加载完成(不阻塞全局渲染)
如果不想显示全局加载页面,可以在需要数据的页面组件中,等待数据服务的加载任务完成,再执行后续逻辑。
- 优化Scoped数据服务,添加TaskCompletionSource
public class AppDataService { private readonly TaskCompletionSource<bool> _loadCompletion = new(); // 对外暴露加载完成的Task public Task DataLoadedTask => _loadCompletion.Task; public UserPreferences Preferences { get; private set; } public async Task LoadFromProtectedLocalStorage(ProtectedLocalStorage localStorage) { var storageResult = await localStorage.GetAsync<UserPreferences>("user-preferences"); Preferences = storageResult.Success ? storageResult.Value : new UserPreferences(); // 标记加载完成 _loadCompletion.SetResult(true); } }
- 在页面组件中等待加载完成
@inject AppDataService DataService <h1>用户设置</h1> <p>主题:@DataService.Preferences.Theme</p> @code { protected override async Task OnInitializedAsync() { // 等待数据加载完成 await DataService.DataLoadedTask; // 之后可以安全使用DataService中的数据 } }
这种方式不需要重复编写加载逻辑,所有需要数据的页面只需要调用await DataService.DataLoadedTask即可,保证拿到的是已加载完成的数据。
关键结论
- 不能直接强制App.Razor的异步方法先于页面组件执行:Blazor的组件生命周期是并行调度的,但可以通过上述两种方案间接实现数据加载完成后再初始化页面组件。
- 不需要在每个页面重复编写加载逻辑:通过Scoped服务统一管理数据加载,页面只需要判断加载状态或等待加载任务即可,完全避免重复代码。
内容的提问来源于stack exchange,提问作者HairyIce
相关产品推荐
相关产品推荐

