Blazor滚动加载异常:文本正常,添加图片组件后仅加载一次
问题描述
在Blazor项目中实现滚动加载功能时出现异常:
- 仅加载文本列表时,滚动到底部可无限触发加载新文本
- 添加图片组件(
FunnyLayout)后,仅能触发一次加载,后续判断滚动到底部的布尔值始终为false
相关代码
host.cshtml中的脚本
<script> function isScrollAtBottom(container) { return container.scrollHeight - container.scrollTop === container.clientHeight; } </script>
页面组件代码
<div @ref="scrollContainer" @onscroll="HandleScroll" style="overflow:scroll; height: 100vh;"> @foreach (var item in FC) { <FunnyLayout ImageId="@item.ImageId" ImageTitle="@item.ImageTitle" /> <div class="spacer"></div> } @foreach (var item in items) { <p>@item</p> } </div> <style> .spacer{ height: 40px; } </style> @code { private List<string> items = new List<string> { "Item 1", "Item 2", "Item 3" }; private List<FunnyContent> FC = new List<FunnyContent> { new FunnyContent{ImageId = "gif", ImageTitle = "il neggtt"}, new FunnyContent{ImageId = "1", ImageTitle = "was geht"}, new FunnyContent{ImageId = "2", ImageTitle = "ma boy"}, }; private ElementReference scrollContainer; [Inject] private IJSRuntime JSRuntime { get; set; } protected override async Task OnInitializedAsync() { } private async Task HandleScroll() { var isAtBottom = await JSRuntime.InvokeAsync<bool>("isScrollAtBottom", scrollContainer); if (isAtBottom) { LoadMoreContent(); } } private void LoadMoreContent() { items.Add("New Item"); items.Add("Another New Item"); var x = new FunnyContent { ImageId = "1", ImageTitle = "himom0" }; FC.Add(x); FC.Add(x); FC.Add(x); } private class FunnyContent { public string ImageId { get; set; } public string ImageTitle { get; set; } } }
原因分析
核心问题出在图片加载的异步性和严格的滚动判断逻辑:
- 文本内容是即时渲染的,添加新文本后DOM立刻更新,
scrollHeight同步变大,此时滚动位置刚好处于底部,严格相等的判断条件成立,能继续触发加载。 - 图片组件中的图片是异步加载的:调用
LoadMoreContent添加图片组件后,Blazor先渲染图片占位DOM,但图片实际还在加载中,此时scrollHeight是占位高度。等图片加载完成后,图片容器高度变大,scrollHeight突然增加,但滚动位置仍停留在之前的位置,导致scrollHeight - scrollTop大于clientHeight,严格相等的条件不再满足,后续滚动判断始终返回false。
解决方案
1. 放宽滚动到底部的判断条件
把严格相等改为允许微小误差,避免因图片加载导致的scrollHeight变化影响判断:
function isScrollAtBottom(container) { // 允许10px以内的误差,视为已到达底部 return container.scrollHeight - container.scrollTop <= container.clientHeight + 10; }
2. 监听图片加载完成事件
在FunnyLayout组件中给图片添加onload事件,当图片加载完成后通知父组件重新检查滚动状态:
FunnyLayout.razor中:
<img src="@GetImageUrl(ImageId)" alt="@ImageTitle" @onload="NotifyImageLoaded" /> @code { [Parameter] public string ImageId { get; set; } [Parameter] public string ImageTitle { get; set; } [Parameter] public EventCallback OnImageLoaded { get; set; } private string GetImageUrl(string id) { // 返回图片资源路径 return $"/images/{id}.jpg"; } private async Task NotifyImageLoaded() { await OnImageLoaded.InvokeAsync(); } }
- 父组件修改调用:
<FunnyLayout ImageId="@item.ImageId" ImageTitle="@item.ImageTitle" OnImageLoaded="CheckScrollPosition" />
- 父组件添加检查方法:
private async Task CheckScrollPosition() { var isAtBottom = await JSRuntime.InvokeAsync<bool>("isScrollAtBottom", scrollContainer); if (isAtBottom) { LoadMoreContent(); } }
3. 优化滚动事件触发频率(可选)
添加节流函数减少滚动事件的触发次数,避免频繁调用JS互操作:
let scrollTimeout; function handleScrollThrottled(container, callback) { clearTimeout(scrollTimeout); scrollTimeout = setTimeout(() => { const isAtBottom = container.scrollHeight - container.scrollTop <= container.clientHeight + 10; callback(isAtBottom); }, 100); }
内容的提问来源于stack exchange,提问作者innom
相关产品推荐
相关产品推荐

