Blazor页面未完成渲染前导航触发JSInterop异常问题排查
Blazor未完成图表渲染时导航抛出JS异常的解决方案
当页面触发导航时,当前组件会被立即销毁,但OnAfterRenderAsync中正在执行的Blazor Bootstrap图表初始化JS互操作仍在尝试访问已被移除的DOM元素,从而抛出Cannot read properties of null (reading 'id')异常。以下是无需等待渲染完成即可安全导航的解决方案:
方案1:用取消令牌终止未完成的JS操作
在图表组件中引入CancellationTokenSource,在组件销毁时取消未完成的初始化任务,避免无效的DOM访问:
@implements IDisposable <BlazorBootstrap.DoughnutChart @ref="CurrentDonut" /> @code { [Inject] private NavigationManager NavManager { get; set; } = default!; private DoughnutChart? CurrentDonut; private CancellationTokenSource? _cts; protected override void OnInitialized() { _cts = new CancellationTokenSource(); // 配置图表数据与选项 CurrentUsageData = /* 你的数据逻辑 */; CurrentUsageOptions = /* 你的选项配置 */; base.OnInitialized(); } protected override async Task OnAfterRenderAsync(bool firstRender) { if (firstRender && _cts is not null && !_cts.Token.IsCancellationRequested) { try { // 传递取消令牌到图表初始化方法 await CurrentDonut!.InitializeAsync(CurrentUsageData, CurrentUsageOptions, cancellationToken: _cts.Token); } catch (OperationCanceledException) { // 组件已销毁,静默忽略取消异常 } } await base.OnAfterRenderAsync(firstRender); } public void Dispose() { // 取消未完成的异步任务 _cts?.Cancel(); _cts?.Dispose(); // 清理图表实例(Blazor Bootstrap组件支持Dispose) CurrentDonut?.Dispose(); } private ChartData CurrentUsageData = default!; private ChartOptions CurrentUsageOptions = default!; }
方案2:给导航按钮添加状态控制
通过状态标记防止重复点击,同时在导航前锁定操作,避免无效的后续执行:
<button @onclick="Navigate" disabled="@_isNavigating">跳转到仪表盘</button> @code { [Inject] private NavigationManager NavManager { get; set; } = default!; private bool _isNavigating; private void Navigate() { if (_isNavigating) return; _isNavigating = true; NavManager.NavigateTo("/Dashboard"); // 导航后组件会立即销毁,无需重置_isNavigating } }
方案3:监听导航事件跳过初始化
通过NavigationManager的导航事件标记组件状态,跳过后续的图表初始化逻辑:
@implements IDisposable <BlazorBootstrap.DoughnutChart @ref="CurrentDonut" /> @code { [Inject] private NavigationManager NavManager { get; set; } = default!; private DoughnutChart? CurrentDonut; private bool _hasNavigated; protected override void OnInitialized() { NavManager.LocationChanged += OnLocationChanged; // 配置图表数据与选项 CurrentUsageData = /* 你的数据逻辑 */; CurrentUsageOptions = /* 你的选项配置 */; base.OnInitialized(); } protected override async Task OnAfterRenderAsync(bool firstRender) { if (firstRender && !_hasNavigated) { try { await CurrentDonut!.InitializeAsync(CurrentUsageData, CurrentUsageOptions); } catch (JSException) { // 捕获DOM不存在的异常,静默处理 } } await base.OnAfterRenderAsync(firstRender); } private void OnLocationChanged(object? sender, LocationChangedEventArgs e) { _hasNavigated = true; } public void Dispose() { NavManager.LocationChanged -= OnLocationChanged; CurrentDonut?.Dispose(); } private ChartData CurrentUsageData = default!; private ChartOptions CurrentUsageOptions = default!; }
以上方案均无需等待所有组件渲染完成即可安全导航,同时避免了forceReload参数导致的用户认证丢失问题。
内容的提问来源于stack exchange,提问作者donny_V
相关产品推荐
相关产品推荐

