Blazor 8中Razor页面时钟无法自动运行,如何解决?
Blazor SSR模式下时钟无法自动运行的解决方案
问题背景
在Blazor项目中已设置全局交互式服务器渲染模式,Clock Razor页面的时钟仅在手动刷新页面时正常显示,刷新后无法自动更新。已通过NuGet安装moment.js,尝试两种方案均未解决问题。
相关代码
Clock Razor页面
<h1>Current</h1> <div id="clock"></div> <script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.1/moment.min.js"> <script> function updateClock() { var now = moment().format('YYYY-MM-DD HH:mm:ss'); document.getElementById('clock').innerText = now; } updateClock(); setInterval(updateClock, 1000); </script>
Program.cs 渲染模式配置
app.MapRazorComponents<App>() .AddInteractiveServerRenderMode();
尝试过的无效方案
方案一:通过IJSRuntime调用setInterval
[Inject] private IJSRuntime JSRuntime { get; set; } protected override async Task OnInitializedAsync() { await JSRuntime.InvokeVoidAsync("setInterval"); }
方案二:添加额外程序集
app.MapRazorComponents<App>() .AddAdditionalAssemblies();
问题根源
- 原生
<script>标签语法错误:第一个引入moment.js的script标签未闭合,导致后续JS代码无法正确解析执行。 - Blazor交互式服务器渲染模式下,组件内的原生JS代码执行时机与组件生命周期不匹配,组件初始化时DOM可能尚未就绪,JS逻辑无法绑定生效。
- 之前的方案逻辑错误:方案一未正确传递
setInterval所需的参数;方案二的AddAdditionalAssemblies()未传入有效程序集,与JS执行无关。
解决步骤
1. 修复Script标签语法错误
确保引入moment.js的script标签正确闭合:
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.1/moment.min.js"></script>
2. 使用JS隔离执行时钟逻辑
通过Blazor的JS隔离机制,在组件渲染完成后初始化时钟,确保DOM元素已存在:
步骤一:创建独立JS文件
在wwwroot/js目录下新建clock.js,封装时钟初始化逻辑:
export function initClock() { function updateClock() { const now = moment().format('YYYY-MM-DD HH:mm:ss'); document.getElementById('clock').textContent = now; } // 初始化显示当前时间 updateClock(); // 设置每秒更新并返回定时器ID,用于销毁时清除 return setInterval(updateClock, 1000); } export function stopClock(intervalId) { clearInterval(intervalId); }
步骤二:修改Clock Razor组件
在组件中注入IJSRuntime,在OnAfterRenderAsync(组件渲染到DOM后触发)中调用JS函数,并实现销毁逻辑避免内存泄漏:
@inject IJSRuntime JSRuntime @implements IAsyncDisposable <h1>Current</h1> <div id="clock"></div> @code { private IJSObjectReference? _jsModule; private int? _intervalId; protected override async Task OnAfterRenderAsync(bool firstRender) { if (firstRender) { // 导入JS模块 _jsModule = await JSRuntime.InvokeAsync<IJSObjectReference>( "import", "./js/clock.js"); // 初始化时钟并保存定时器ID _intervalId = await _jsModule.InvokeAsync<int>("initClock"); } } // 组件销毁时清除定时器 public async ValueTask DisposeAsync() { if (_jsModule is not null && _intervalId.HasValue) { await _jsModule.InvokeVoidAsync("stopClock", _intervalId.Value); await _jsModule.DisposeAsync(); } } }
3. 确保moment.js全局可用
如果不想使用ES模块导入,可在项目的布局文件(如_Layout.cshtml或根组件的布局)中全局引入moment.js,确保JS代码能直接调用:
<!-- 在布局文件的<body>末尾添加 --> <script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.1/moment.min.js"></script>
为什么之前的方案无效?
- 方案一:直接调用
JSRuntime.InvokeVoidAsync("setInterval")未传入回调函数和间隔时间参数,不符合JSsetInterval的调用规范,无法触发时间更新逻辑。 - 方案二:
AddAdditionalAssemblies()用于添加包含Blazor组件的额外程序集,与JS代码执行无关,对时钟问题没有帮助。
内容的提问来源于stack exchange,提问作者sayditto
相关产品推荐
相关产品推荐

