Blazor Server:ServerPrerendered模式下OnInitializedAsync重复执行的优化咨询
Blazor Server ServerPrerendered模式下OnInitializedAsync重复执行的处理问题
一、防重复处理是否有必要?
这取决于你的业务逻辑类型:
- 写操作场景:如果
OnInitializedAsync中包含插入、更新、删除等写逻辑,两次执行会导致数据重复(比如重复创建记录),这种情况下必须做防重复处理,否则会引发数据一致性问题。 - 只读查询场景:如果只是读取数据,两次执行不会破坏数据,但会额外增加服务器资源消耗(比如重复数据库查询)。在高并发场景下,重复查询会加重数据库负载,此时做防重复处理能有效优化性能;如果是低流量站点,不处理也不会有致命问题,但仍建议优化。
二、更优雅的实现方式
1. 抽离缓存逻辑到服务层(推荐)
将数据获取和缓存逻辑从组件中分离到独立的业务服务中,符合关注点分离原则,也让组件代码更简洁:
// 业务服务类 public class PostService { private readonly IDbFactory _dbFactory; private readonly IMemoryCache _memoryCache; private const string PostListCacheKey = "Index_PostList"; public PostService(IDbFactory dbFactory, IMemoryCache memoryCache) { _dbFactory = dbFactory; _memoryCache = memoryCache; } public async Task<List<PostInfo>?> GetPostListAsync() { return await _memoryCache.GetOrCreateAsync(PostListCacheKey, async entry => { // 设置缓存过期时间 entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(30); using var context = await _dbFactory.CreateDbContextAsync(); // 执行实际查询逻辑 return await context.Posts.ToListAsync(); }); } }
组件中注入服务并调用:
@inject PostService PostService @code { private List<PostInfo>? ListNewCode; protected override async Task OnInitializedAsync() { ListNewCode = await PostService.GetPostListAsync(); } }
2. 基于预渲染状态跳过执行(适合不需要预渲染数据的场景)
如果你的页面不需要预渲染填充数据,可以判断当前是否处于预渲染阶段,跳过数据加载逻辑。但这种方式会让预渲染的HTML为空,失去预渲染的SEO优势,仅适合对SEO要求不高的场景:
@inject IHttpContextAccessor HttpContextAccessor @code { private List<PostInfo>? ListNewCode; protected override async Task OnInitializedAsync() { // 判断是否处于预渲染阶段 var isPrerendering = HttpContextAccessor.HttpContext?.Items.ContainsKey("__prerendering") == true && (bool)HttpContextAccessor.HttpContext.Items["__prerendering"]; if (isPrerendering) return; ListNewCode = await GetList(); } private async Task<List<PostInfo>?> GetList() { using var context = await DbFactory.CreateDbContextAsync(); // 查询逻辑 return await context.Posts.ToListAsync(); } }
3. 使用Scoped服务存储临时数据
利用Scoped服务的生命周期(与用户会话绑定),在第一次执行时存储数据,后续直接复用。不过这种方式要注意,预渲染阶段的Scoped服务和交互式渲染阶段的服务是不同实例,所以需要配合缓存或其他标记:
// Scoped服务 public class PostDataHolder { public List<PostInfo>? PostList { get; set; } } // 组件中使用 @inject PostDataHolder DataHolder @code { private List<PostInfo>? ListNewCode; protected override async Task OnInitializedAsync() { if (DataHolder.PostList != null) { ListNewCode = DataHolder.PostList; return; } ListNewCode = await GetList(); DataHolder.PostList = ListNewCode; } }
这种方式适合不需要持久化缓存,仅在当前会话内复用数据的场景。
内容的提问来源于stack exchange,提问作者King Lee
相关产品推荐
相关产品推荐

