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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.24 21:06:08