我正在使用最新的Blazor WebAssembly 3.2.0 候选版本
从 vanilla Blazor WASM 模板中,我进行了以下调整以测试/调试问题:
在Program.cs中,我添加了以下将注入所有页面的单例:
builder.Services.AddSingleton<ApplicationState>();
该类看起来像:
public class ApplicationState
{
public bool BoolValue { get; set; }
public string StringValue { get; set; }
public ApplicationState()
{
BoolValue = false;
StringValue = "Default value";
}
}
并与JSRuntime一起注入到_Imports.razor中,因此它们可用于所有页面:
@inject IJSRuntime JSRuntime
@inject BlazorApp1.State.ApplicationState AppState;
为了测试 ApplicationState 对象是否正确地通过了应用程序,我在以下 2 个位置使用了它:
在NavManu.razor中,我将其中一个菜单项包裹在BoolValue周围以显示/隐藏该项目:
@if (AppState.BoolValue)
{
<li class="nav-item px-3">
<NavLink class="nav-link" href="counter">
<span class="oi oi-plus" aria-hidden="true"></span> Counter
</NavLink>
</li>
}
在Index.razor中,我在默认的 Hello World 标头下添加了一个 H2 标记以显示StringValue
<h1>Hello, world!</h1>
<h2>@AppState.StringValue</h2>
我还有一个位于wwwroot/js/extenstions.js的 javascript 文件,它有一个用于获取 cookie 数据的函数:
function getCookie(name) {
var nameEQ = name + "=";
var ca = document.cookie.split(';');
for (var i = 0; i < ca.length; i++) {
var c = ca[i];
while (c.charAt(0) == ' ') c = c.substring(1, c.length);
if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length, c.length);
}
return null;
};
并且作为脚本包含在_framework/blazor.webassembly.js脚本下方的wwwroot/index.html中:
<script src="_framework/blazor.webassembly.js"></script>
<script src="js/extensions.js"></script>
在App.razor我有以下@code块:
@code
{
protected override async Task OnInitializedAsync()
{
AppState.BoolValue = true;
AppState.StringValue = "Testing 123...";
}
}
一切都按预期工作,菜单项以及 H2 标记中的字符串值都是可见的。
但是,如果我通过 JSRuntime 添加对我的 Javasript 函数的调用,如下所示:
@code
{
protected override async Task OnInitializedAsync()
{
var jwtToken = await JSRuntime.InvokeAsync<string>("getCookie", "cookieName");
AppState.BoolValue = true;
AppState.StringValue = "Testing 123...";
}
}
javascript运行,AppState值被更新(我添加了一个控制台日志来测试这个)但是导航没有更新以显示菜单项并且H2标签没有显示新的字符串值......
如果您导航到菜单中的页面,状态将更新,但如果您进行硬刷新,则不会再次更新......
我尝试添加StateHasChanged()调用,但这不起作用:
@code
{
protected override async Task OnInitializedAsync()
{
var jwtToken = await JSRuntime.InvokeAsync<string>("getCookie", "cookieName");
AppState.BoolValue = true;
AppState.StringValue = "Testing 123...";
StateHasChanged(); // Does not work!
}
}
由于导航后状态似乎更新了,我也尝试强制导航事件,也无济于事:
@code
{
protected override async Task OnInitializedAsync()
{
var jwtToken = await JSRuntime.InvokeAsync<string>("getCookie", "cookieName");
AppState.BoolValue = true;
AppState.StringValue = "Testing 123...";
var uri = new Uri(NavigationManager.Uri).GetComponents(UriComponents.PathAndQuery, UriFormat.Unescaped);
NavigationManager.NavigateTo(uri);
}
}
我正在继续尝试其他一些事情,但似乎没有任何效果。在我的理解中, StateHasChanged()确实应该是解决此类问题的方法。还有什么我应该尝试的吗?
更新
正如 enet 指出的那样,当状态发生变化时,我需要通知下游组件。引起问题的不是 JSRuntime,而是它在 App.razor 中创建的延迟导致对象状态在硬刷新时似乎有所不同。在第一个场景中,对象在其他组件开始渲染之前更新,因此它们使用已经更新的状态。但是一旦出现任何类型的延迟(例如调用 JSRuntime),他们就需要通知来更新他们的状态,因为他们有机会在延迟之前使用旧状态进行渲染。我用几毫秒的 Task.Delay() 替换了 JSRuntime 调用,这导致了同样的问题!向事件添加触发器解决了这个问题。