Blazor WASM组件渲染问题:SignalR回调首次无法更新DOM如何解决
问题原因
- 生命周期时机错误:你当前在
OnInitializedAsync中调用JS的Connect方法,这个生命周期阶段Blazor还未将Chat组件的DOM元素挂载到页面文档中,SignalR连接建立成功后触发的GetWeather返回推送事件时,$('#temp').text(temperature)根本找不到#temp元素,首次赋值失败。 - 全局JS变量残留+重复注册:JS模块顶层的
SignalrConnection、ChatProxy是全局变量,第一次加载组件时创建的连接和回调没有随着组件销毁而释放。当你跳转其他页面再返回Chat组件时,新的组件实例已经完成DOM渲染,此时重新调用Connect会覆盖全局变量、注册新的回调,此时#temp元素已经存在,所以赋值可以正常生效。 - 直接操作DOM不符合Blazor渲染逻辑:你通过jQuery直接修改DOM内容,Blazor本身并不知道这个状态变化,如果Blazor触发组件重渲染,会用C#中保存的
temperature值覆盖你JS修改的DOM内容,也会导致显示异常。 - 预渲染兼容问题:如果项目开启了预渲染,首次加载时服务端返回的静态
#temp元素会在Blazor接管DOM时被销毁替换,你首次注册的回调操作的是已经被删除的旧DOM元素,自然不会生效。
解决方案
建议调整为Blazor状态驱动的渲染逻辑,避免直接操作DOM,同时调整初始化时机、补充资源释放逻辑,修改后的代码如下:
修改后的C#后置代码
using Microsoft.AspNetCore.Components; using Microsoft.JSInterop; using System.Threading.Tasks; namespace BlazorClientJS.Pages { public partial class Chat : IAsyncDisposable { [Inject] IJSRuntime JS { get; set; } string temperature = string.Empty; IJSObjectReference module; private DotNetObjectReference<Chat>? _dotNetRef; protected override void OnInitialized() { _dotNetRef = DotNetObjectReference.Create(this); } protected async override Task OnAfterRenderAsync(bool firstRender) { if (firstRender) { module = await JS.InvokeAsync<IJSObjectReference>("import", "./Scripts/changeweather.js"); await module.InvokeVoidAsync("Connect", _dotNetRef); } } [JSInvokable] public void UpdateTemperature(string temp) { temperature = temp; StateHasChanged(); } async Task ChangeWeatherAsync(string temperature) { if(module != null) { await module.InvokeVoidAsync("ChangeWeather", temperature); } } public async ValueTask DisposeAsync() { if (module != null) { await module.InvokeVoidAsync("Disconnect"); await module.DisposeAsync(); } _dotNetRef?.Dispose(); } } }
修改后的JS代码
var SignalrConnection; var ChatProxy; var dotNetRef; export function ChangeWeather(temperature) { ChatProxy.invoke('change_weather', temperature); } export function GetWeather() { ChatProxy.invoke('get_weather'); } export function Connect(ref) { dotNetRef = ref; var ChatServerUrl = "http://localhost:8080/"; var ChatUrl = ChatServerUrl + "signalR"; SignalrConnection = $.hubConnection(ChatUrl, { useDefaultPath: false }); ChatProxy = SignalrConnection.createHubProxy('ChatHub'); ChatProxy.on("NotifyUser", function (temperature) { dotNetRef.invokeMethodAsync('UpdateTemperature', temperature.toString()); }); SignalrConnection.start().done(function () { GetWeather(); alert("Connected to Signalr Server"); }) .fail(function () { alert("failed in connecting to the signalr server"); }) } export function Disconnect() { if (SignalrConnection) { SignalrConnection.stop(); } SignalrConnection = null; ChatProxy = null; dotNetRef = null; }
修改后的Razor页面代码
<div style="text-align:center;"> <input type="text" id="txtTemperature" @bind-value="@temperature"/> <button id="btnChange" @onclick="@(() => ChangeWeatherAsync(temperature))">Change Weather</button> </div> <div style="text-align:center;"> Temperature : <span>@temperature</span> ℃ </div>
内容的提问来源于stack exchange,提问作者Alex Ivan
相关产品推荐
相关产品推荐

