在我的blazer应用程序中,我正在对后端服务器进行api调用,这可能需要一些时间。我需要向用户显示反馈,等待光标或“旋转”图像。在Blazor中如何进行?
我尝试使用CSS并打开和关闭CSS,但是直到调用完成,页面才会刷新。任何建议将不胜感激。
@functions {
UserModel userModel = new UserModel();
Response response = new Response();
string errorCss = "errorOff";
string cursorCSS = "cursorSpinOff";
protected void Submit()
{
//Show Sending...
cursorCSS = "";
this.StateHasChanged();
response = Service.Post(userModel);
if (response.Errors.Any())
{
errorCss = "errorOn";
}
//turn sending off
cursorCSS = "cursorSpinOff";
this.StateHasChanged();
}
}
Run Code Online (Sandbox Code Playgroud)
Hen*_*man 12
除了@dani 的回答之外,我还想指出这里有两个独立的问题,将它们分开是值得的。
StateHasChanged()Blazor 将(概念上)在初始化之后以及事件之前和之后调用 StateHasChanged()。这意味着您通常不需要调用它,只有当您的方法有几个不同的步骤并且您想要在中间更新 UI 时才需要调用它。旋转器就是这种情况。
async void当您使用“即发即弃”(fire-and-forget) ( ) 或当更改来自不同的源(例如计时器或来自程序中另一层的事件)时,您确实需要调用它。
StateHasChanged()StateHasChanged() 本身不会更新 UI。它只是将渲染操作排队。您可以将其视为设置“脏标志”。
一旦渲染引擎再次在其线程上运行,Blazor 将更新 UI。与任何其他 UI 框架非常相似,所有 UI 操作都必须在主线程上完成。但是您的事件也(最初)在同一线程上运行,从而阻塞渲染器。
要解决此问题,请通过返回 来确保您的事件是异步的 async Task。Blazor 完全支持这一点。不使用。 仅当您不需要异步行为时才async void使用。void
2.1 使用异步操作
当您的方法在 StateHasChanged() 之后快速等待异步 I/O 操作时,您就完成了。控制权将返回到渲染引擎,并且您的 UI 将更新。
statusMessage = "Busy...";
StateHasChanged();
response = await SomeLongCodeAsync(); // show Busy
statusMessage = "Done.";
Run Code Online (Sandbox Code Playgroud)
2.2 插入一个小的异步动作
当您的代码是 CPU 密集型时,它不会快速释放主线程。当您调用某些外部代码时,您并不总是知道它到底有多“异步”。所以我们有一个流行的技巧:
statusMessage = "Busy...";
StateHasChanged();
await Task.Delay(1); // flush changes - show Busy
SomeLongSynchronousCode();
statusMessage = "Done.";
Run Code Online (Sandbox Code Playgroud)
更合乎逻辑的版本是使用Task.Yield(),但在 WebAssembly 上往往会失败。
2.3 使用额外的线程Task.Run()
当您的事件处理程序需要调用一些非异步代码(例如 CPU 密集型工作)并且您位于 Blazor-Server 上时,您可以使用 Task.Run() 申请一个额外的线程池:
statusMessage = "Busy...";
StateHasChanged();
await Task.Run( _ => SomeLongSynchronousCode()); // run on other thread
statusMessage = "Done.";
Run Code Online (Sandbox Code Playgroud)
当您在 Blazor-WebAssembly 上运行它时,它没有任何效果。浏览器环境中没有可用的“额外线程”。
当您在 Blazor-Server 上运行此程序时,您应该意识到使用更多线程可能会损害您的可扩展性。如果您计划在服务器上运行尽可能多的并发客户端,那么这是一种反优化。
当你想尝试时:
void SomeLongSynchronousCode()
{
Thread.Sleep(3000);
}
Task SomeLongCodeAsync()
{
return Task.Delay(3000);
}
Run Code Online (Sandbox Code Playgroud)
Bri*_*ana 11
围绕 StateHasChanged() 有很多精彩的讨论,但为了回答 OP 的问题,这里有另一种普遍实现旋转器的方法,用于 HttpClient 对后端 API 的调用。
此代码来自 Blazor WebAssembly 应用程序...
程序.cs
public static async Task Main(string[] args)
{
var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.Add<App>("#app");
builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
builder.Services.AddScoped<SpinnerService>();
builder.Services.AddScoped<SpinnerHandler>();
builder.Services.AddScoped(s =>
{
SpinnerHandler spinHandler = s.GetRequiredService<SpinnerHandler>();
spinHandler.InnerHandler = new HttpClientHandler();
NavigationManager navManager = s.GetRequiredService<NavigationManager>();
return new HttpClient(spinHandler)
{
BaseAddress = new Uri(navManager.BaseUri)
};
});
await builder.Build().RunAsync();
}
Run Code Online (Sandbox Code Playgroud)
SpinnerHandler.cs
注意:请记住取消注释人为延迟。如果您在 Visual Studio 中使用现成的 Webassemble 模板,请单击“天气预报”以查看微调器的实际操作演示。
public class SpinnerHandler : DelegatingHandler
{
private readonly SpinnerService _spinnerService;
public SpinnerHandler(SpinnerService spinnerService)
{
_spinnerService = spinnerService;
}
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
_spinnerService.Show();
//await Task.Delay(3000); // artificial delay for testing
var response = await base.SendAsync(request, cancellationToken);
_spinnerService.Hide();
return response;
}
}
Run Code Online (Sandbox Code Playgroud)
SpinnerService.cs
public class SpinnerService
{
public event Action OnShow;
public event Action OnHide;
public void Show()
{
OnShow?.Invoke();
}
public void Hide()
{
OnHide?.Invoke();
}
}
Run Code Online (Sandbox Code Playgroud)
MainLayout.razor
@inherits LayoutComponentBase
<div class="page">
<div class="sidebar">
<NavMenu />
</div>
<div class="main">
<div class="top-row px-4">
<a href="http://blazor.net" target="_blank" class="ml-md-auto">About</a>
</div>
<div class="content px-4">
@Body
<Spinner />
</div>
</div>
</div>
Run Code Online (Sandbox Code Playgroud)
Spinner.razor
注意:要添加一些变化,您可以在 OnIntialized() 方法中生成一个随机数,并在 div 内使用 switch 语句来选择随机微调器类型。在此方法中,对于每个 HttpClient 请求,最终用户将观察到随机旋转器类型。为了简洁起见,该示例已被修剪为仅一种类型的旋转器。
@inject SpinnerService SpinnerService
@if (isVisible)
{
<div class="spinner-container">
<Spinner_Wave />
</div>
}
@code
{
protected bool isVisible { get; set; }
protected override void OnInitialized()
{
SpinnerService.OnShow += ShowSpinner;
SpinnerService.OnHide += HideSpinner;
}
public void ShowSpinner()
{
isVisible = true;
StateHasChanged();
}
public void HideSpinner()
{
isVisible = false;
StateHasChanged();
}
public void Dispose()
{
SpinnerService.OnShow -= ShowSpinner;
SpinnerService.OnHide -= HideSpinner;
}
}
Run Code Online (Sandbox Code Playgroud)
Spinner-Wave.razor
来源:https ://tobiasahlin.com/spinkit/
注意:此旋转套件有一个 Nuget 包。Nuget 包的缺点是您无法直接访问 CSS 来进行调整。在这里,我调整了微调器的大小,并设置背景颜色以匹配网站的主要颜色,如果您在整个网站中使用 CSS 主题(或者可能是多个 CSS 主题),这会很有帮助
@* Credit: https://tobiasahlin.com/spinkit/ *@
<div class="spin-wave">
<div class="spin-rect spin-rect1"></div>
<div class="spin-rect spin-rect2"></div>
<div class="spin-rect spin-rect3"></div>
<div class="spin-rect spin-rect4"></div>
<div class="spin-rect spin-rect5"></div>
</div>
<div class="h3 text-center">
<strong>Loading...</strong>
</div>
<style>
.spin-wave {
margin: 10px auto;
width: 200px;
height: 160px;
text-align: center;
font-size: 10px;
}
.spin-wave .spin-rect {
background-color: var(--primary);
height: 100%;
width: 20px;
display: inline-block;
-webkit-animation: spin-waveStretchDelay 1.2s infinite ease-in-out;
animation: spin-waveStretchDelay 1.2s infinite ease-in-out;
}
.spin-wave .spin-rect1 {
-webkit-animation-delay: -1.2s;
animation-delay: -1.2s;
}
.spin-wave .spin-rect2 {
-webkit-animation-delay: -1.1s;
animation-delay: -1.1s;
}
.spin-wave .spin-rect3 {
-webkit-animation-delay: -1s;
animation-delay: -1s;
}
.spin-wave .spin-rect4 {
-webkit-animation-delay: -0.9s;
animation-delay: -0.9s;
}
.spin-wave .spin-rect5 {
-webkit-animation-delay: -0.8s;
animation-delay: -0.8s;
}
@@-webkit-keyframes spin-waveStretchDelay {
0%, 40%, 100% {
-webkit-transform: scaleY(0.4);
transform: scaleY(0.4);
}
20% {
-webkit-transform: scaleY(1);
transform: scaleY(1);
}
}
@@keyframes spin-waveStretchDelay {
0%, 40%, 100% {
-webkit-transform: scaleY(0.4);
transform: scaleY(0.4);
}
20% {
-webkit-transform: scaleY(1);
transform: scaleY(1);
}
}
</style>
Run Code Online (Sandbox Code Playgroud)
很美丽
Blazor使用虚拟dom,该框架跟踪更改并仅在不阻止主线程时发送更改。这就是我允许Blazor将更改刷新到UI的方式:
async功能。await Task.Delay(1))例
async Task AsyncLongFunc() // this is an async task
{
spinning=true;
await Task.Delay(1); // changes are flushed
LongFunc(); // usually with a wait ( is a web request)
currentCount++;
spinning=false;
await Task.CompletedTask; // just to avoid non await warning.
}
Run Code Online (Sandbox Code Playgroud)
如您所见,StateHasChanged不需要。
注意:如果有更简单的方法或更优雅的解决方案,请使用IDK。
影响:
整页代码(已编辑,符合netcore3 Preview6):
@page "/counter"
<h1>Counter</h1>
<p>Current count:
@(spinning?"Incrementing .... (the spinning effect)":currentCount.ToString())
</p>
<button class="btn btn-primary"
@onclick="@IncrementCount">Click me</button>
<button class="btn @(spinning?"btn-dark":"btn-primary") "
@onclick="@AsyncLongFunc">Click me Async</button>
@code {
int currentCount = 0;
bool spinning = false;
void IncrementCount()
{
currentCount++;
}
async Task AsyncLongFunc()
{
spinning=true;
await Task.Delay(1);
LongFunc();
currentCount++;
spinning=false;
await Task.CompletedTask;
}
void LongFunc() => Task.Delay(2000).Wait();
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1337 次 |
| 最近记录: |