You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 20:35:32