Blazor应用多JSON下载解析致加载慢、交互卡顿问题咨询
问题根本原因
你的页面加载慢、交互无响应全是代码写法错误导致的,和NuGet包、配置没有关系,核心问题有以下几个:
- 渲染模板中写阻塞式异步调用,直接卡死UI线程
你在Razor的@foreach渲染循环里直接写@Task.Run(...).Result是最致命的问题。Blazor的渲染、UI事件响应全部跑在同一个同步上下文里,.Result会直接阻塞这个线程,直到对应HTTP请求返回才会继续往下走。
这意味着:渲染N个玩家,就要串行等待2*N个第三方接口请求全部跑完,才能完成一次渲染。这期间所有按钮点击、弹窗交互全部要排队等渲染结束,自然点啥都卡半天。
而且渲染函数的唯一职责是把已经加载好的数据映射成HTML,绝对不能在渲染过程中发起网络请求、做数据计算这类带副作用的慢操作,你这个写法完全违反了Blazor组件的生命周期设计。 - 多线程写法属于负优化,还存在死锁风险
你在GetMatchesHistory里手动new Thread、靠while(dnl=="")轮询等待结果的写法没有任何正向作用,平白无故多开无意义线程、空耗CPU调度资源,而且子线程里调用异步方法还用.Result,很容易触发同步上下文死锁,进一步拖慢速度。 - HttpClient用法错误,导致请求性能越来越差
你每次发请求都新建一个HttpClient实例,会导致套接字耗尽问题,请求延迟会越来越高,甚至出现随机超时。HttpClient本身是设计为单例复用的,不能每次用都new。 - 定时器逻辑持续触发全量重渲染,雪上加霜
你开了个2秒触发一次的定时器,只要检测到数据库玩家数变化就调用StateHasChanged触发重渲染,而每次重渲染又会重新跑一遍所有玩家的串行接口请求,等于后台每2秒就要把所有第三方接口重新请求一遍,页面永远处于“加载-卡顿-重加载”的死循环。而且你没有在组件销毁的时候停掉定时器,会造成内存泄漏,页面开越久越卡。 - 请求全串行无缓存,加载时间随玩家数线性上涨
所有接口请求都是挨个串行执行,没有并行优化,也没有做缓存,同一个玩家的数据每次渲染都要重新请求第三方接口,玩家越多加载越慢,还容易触发第三方接口的限流规则。
修复方案
按以下顺序改完,卡顿问题会直接解决:
- 把所有慢操作全部移出渲染模板
删掉Razor循环里所有Task.Run().Result的代码,所有网络请求、数据计算逻辑全部移到OnInitializedAsync生命周期方法里。正确的加载流程:- 初始化时先从数据库加载玩家列表,此时显示Loading状态
- 并行发起所有玩家的两个接口请求,把胜率、常用文明这些计算好的字段全部填充到玩家模型里
- 所有数据全部加载完成后,再关闭Loading状态,一次性触发渲染,渲染过程只做数据到HTML的转换,不执行任何IO操作
- 删掉所有错误的多线程和阻塞写法
去掉所有手动new Thread的代码,去掉所有.Result、.Wait()这类阻塞调用,异步方法直接用await关键字等待即可,Blazor本身的异步调度不会阻塞UI,不需要你自己包Task.Run开线程。GetMatchesHistory里那个轮询等待的逻辑全删,直接await DownloadStringAsync拿返回值就行。 - 修正HttpClient用法
在Program.cs里通过builder.Services.AddHttpClient()注册HttpClient,然后在组件里通过@inject HttpClient Http注入使用,不要每次请求都new HttpClient。 - 修复定时器逻辑
- 组件实现
IDisposable接口,组件销毁时停掉定时器、解绑Elapsed事件,避免内存泄漏 - 把2秒的刷新间隔调大到15秒以上,你请求的是玩家游戏数据,不需要2秒刷一次,频繁刷新只会被第三方接口限流
- 定时器触发后,不要直接全量重刷所有接口,只拉取新增/变动玩家的数据即可
- 组件实现
- 加缓存优化
给第三方接口的返回结果加5-10分钟的内存缓存,同一个玩家的短时间内重复请求直接读缓存,不需要反复调用接口,加载速度会提升数倍。
改完之后不仅首屏加载速度会大幅提升,按钮点击、弹窗这类交互也会瞬间响应,不会再出现等半天的情况。
内容的提问来源于stack exchange,提问作者Mohammed Amine Belbachir
相关产品推荐
相关产品推荐

