属性如何导致页面在 Blazor 中更新?
How do properties cause pages to update in Blazor?
我刚刚在 Blazor WebAssembly 中完成了我的第一个重要测试应用程序。 Blazor 是令人印象深刻的东西,但我发现很难推断属性更改如何导致更新 DOM - 例如,在 Razor 组件中引用 属性 的地方。
<div>@SomeProperty</div>
public int SomeProperty {get;set;}
在 WPF 中,很容易推断出更改是如何流动的并导致呈现更改,因为它们是由事件和 DependencyProperty 更改触发的。您可以看到这些并绑定到它们。在 Blazor 中,您可以通过某种方式更改 属性 值并更新页面。这背后的精确机制有点像魔法。因此,很难推断如何删除复杂组件的不必要更新。
谁能解释一下这个话题的基础?
是否有任何深入介绍 Blazor 领域的文档或视频?
In Blazor, somehow you can change a property value and the page updates.
不完全是。 属性 本身与此无关。例如,您可以 并且您不会看到 UI 发生变化。
但通常您会设置 属性 以响应 ButtonClick 或其他 Blazor(生命周期)事件。还有那些 are bracketed with calls to StateHasChanged()。在计时器事件或其他 non-Blazor 事件中,您必须自己调用 StateHasChanged()。
Blazor 维护自己的 DOM 副本(快速)。在 StateHasChanged() 之后,该副本被重建并与前一个副本进行比较。任何更改都会应用到实际的 DOM 和 JavaScript。
As a result, it can be hard to reason about how to remove unnecessary updates for complex components.
这与 <div>@SomeProperty</div>
不同。
当您有 <Details ItemId="itemId" />
且 itemId
是基本类型(如 int 或字符串)时,当 itemId 更改时,详细信息将仅 re-rendered。
但是当它是 <Details Item="item" />
并且项目是某种复杂类型时,组件将始终与父页面一起重新呈现。
我有时在 Details 组件中使用以下模式,当 Item 没有可变属性时,即 <Details Item="selectedItem" />
:
[Parameter]
public Item Item { get; set; } = new();
int oldId = 0;
protected override bool ShouldRender()
{
if (oldId != Item.Id)
{
oldId = Item.Id;
return true;
}
return false;
}
Can anyone explain the underpinnings of this topic?
要了解更新过程,您需要了解组件。我会尽量保持简短!
所有组件都必须实现 IComponent
。 ComponentBase
是 IComponent
.
的实现
public interface IComponent
{
void Attach(RenderHandle renderHandle);
Task SetParametersAsync(ParameterView parameters);
}
RenderHandle 的重要部分是:
public readonly struct RenderHandle
{
public Dispatcher Dispatcher ....
//....
public void Render(RenderFragment renderFragment)
{
//....
_renderer.AddToRenderQueue(_componentId, renderFragment);
//...
}
}
RenderFragment 是:
public delegate void RenderFragment(RenderTreeBuilder builder);
A Renderer
管理渲染过程。它包含表示为 RenderTree 的 DOM(浏览器呈现的内容)。当 Renderer 将组件附加到 RenderTree 时,Renderer 创建一个 RenderHandle
并通过调用 Attach
将其传递给组件。 Component 使用此 RenderHandle
与 Renderer 通信。 Renderer 通过调用 SetParametersAsync
.
与组件通信
通过调用 RenderHandle
上的 Render
方法并传递 RenderFragment
委托来“渲染”组件。
这是一个简单的渲染片段:
protected RenderFragment HelloWorld => (RenderTreeBuilder builder) =>
{
builder.OpenElement(0, "div");
builder.AddContent(1, "Hello Blazor");
builder.CloseElement();
};
在 RenderHandle
上调用 Render
不会 渲染组件。它只是将渲染片段放在渲染器的队列中。当渲染器 运行s 片段时,它会检查其他组件引用的组件参数更改。它在引用参数已更改的任何组件上调用 SetParametersAsync
。
StateHasChanged
是一种 ComponentBase
方法。 StateHasChanged
由 Blazor UI 事件处理程序在内部调用,因此您很少需要手动调用它。如果你这样做,问问自己为什么?你的逻辑可能有问题!它看起来像这样:
var task = InvokeAsync(EventMethod);
StateHasChanged();
if (!task.IsCompleted)
{
await task;
StateHasChanged();
}
主要异常是一个正常的事件处理程序。如果事件更新组件中的数据,则需要通过调用 StateHasChanged
触发手动更新。这是标准模式。
private void OnSomethingChanged(object? sender, EventArgs e)
=> this.InvokeAsync(StateHasChanged);
备注:
StateHasChanged
具有检测渲染片段是否已排队的机制,因此它不会对多个渲染进行排队。
InvokeAsync
确保任务在 UI 线程上 运行。它使用 RenderHandle
. 上提供的 Dispatcher
- 当渲染器检查参数更改时,任何对象都被认为是脏的,因为渲染器没有简单的方法来检查相等性。
- 渲染器仅在获得线程时间时才为其队列服务。如果您 运行 有一长串同步代码,比如一个按钮单击处理程序,在同步代码完成之前什么也不会发生。
如果您想进一步挖掘,请深入研究 ComponentBase
- You can view the code here。
我刚刚在 Blazor WebAssembly 中完成了我的第一个重要测试应用程序。 Blazor 是令人印象深刻的东西,但我发现很难推断属性更改如何导致更新 DOM - 例如,在 Razor 组件中引用 属性 的地方。
<div>@SomeProperty</div>
public int SomeProperty {get;set;}
在 WPF 中,很容易推断出更改是如何流动的并导致呈现更改,因为它们是由事件和 DependencyProperty 更改触发的。您可以看到这些并绑定到它们。在 Blazor 中,您可以通过某种方式更改 属性 值并更新页面。这背后的精确机制有点像魔法。因此,很难推断如何删除复杂组件的不必要更新。
谁能解释一下这个话题的基础?
是否有任何深入介绍 Blazor 领域的文档或视频?
In Blazor, somehow you can change a property value and the page updates.
不完全是。 属性 本身与此无关。例如,您可以
但通常您会设置 属性 以响应 ButtonClick 或其他 Blazor(生命周期)事件。还有那些 are bracketed with calls to StateHasChanged()。在计时器事件或其他 non-Blazor 事件中,您必须自己调用 StateHasChanged()。
Blazor 维护自己的 DOM 副本(快速)。在 StateHasChanged() 之后,该副本被重建并与前一个副本进行比较。任何更改都会应用到实际的 DOM 和 JavaScript。
As a result, it can be hard to reason about how to remove unnecessary updates for complex components.
这与 <div>@SomeProperty</div>
不同。
当您有 <Details ItemId="itemId" />
且 itemId
是基本类型(如 int 或字符串)时,当 itemId 更改时,详细信息将仅 re-rendered。
但是当它是 <Details Item="item" />
并且项目是某种复杂类型时,组件将始终与父页面一起重新呈现。
我有时在 Details 组件中使用以下模式,当 Item 没有可变属性时,即 <Details Item="selectedItem" />
:
[Parameter]
public Item Item { get; set; } = new();
int oldId = 0;
protected override bool ShouldRender()
{
if (oldId != Item.Id)
{
oldId = Item.Id;
return true;
}
return false;
}
Can anyone explain the underpinnings of this topic?
要了解更新过程,您需要了解组件。我会尽量保持简短!
所有组件都必须实现 IComponent
。 ComponentBase
是 IComponent
.
public interface IComponent
{
void Attach(RenderHandle renderHandle);
Task SetParametersAsync(ParameterView parameters);
}
RenderHandle 的重要部分是:
public readonly struct RenderHandle
{
public Dispatcher Dispatcher ....
//....
public void Render(RenderFragment renderFragment)
{
//....
_renderer.AddToRenderQueue(_componentId, renderFragment);
//...
}
}
RenderFragment 是:
public delegate void RenderFragment(RenderTreeBuilder builder);
A Renderer
管理渲染过程。它包含表示为 RenderTree 的 DOM(浏览器呈现的内容)。当 Renderer 将组件附加到 RenderTree 时,Renderer 创建一个 RenderHandle
并通过调用 Attach
将其传递给组件。 Component 使用此 RenderHandle
与 Renderer 通信。 Renderer 通过调用 SetParametersAsync
.
通过调用 RenderHandle
上的 Render
方法并传递 RenderFragment
委托来“渲染”组件。
这是一个简单的渲染片段:
protected RenderFragment HelloWorld => (RenderTreeBuilder builder) =>
{
builder.OpenElement(0, "div");
builder.AddContent(1, "Hello Blazor");
builder.CloseElement();
};
在 RenderHandle
上调用 Render
不会 渲染组件。它只是将渲染片段放在渲染器的队列中。当渲染器 运行s 片段时,它会检查其他组件引用的组件参数更改。它在引用参数已更改的任何组件上调用 SetParametersAsync
。
StateHasChanged
是一种 ComponentBase
方法。 StateHasChanged
由 Blazor UI 事件处理程序在内部调用,因此您很少需要手动调用它。如果你这样做,问问自己为什么?你的逻辑可能有问题!它看起来像这样:
var task = InvokeAsync(EventMethod);
StateHasChanged();
if (!task.IsCompleted)
{
await task;
StateHasChanged();
}
主要异常是一个正常的事件处理程序。如果事件更新组件中的数据,则需要通过调用 StateHasChanged
触发手动更新。这是标准模式。
private void OnSomethingChanged(object? sender, EventArgs e)
=> this.InvokeAsync(StateHasChanged);
备注:
StateHasChanged
具有检测渲染片段是否已排队的机制,因此它不会对多个渲染进行排队。InvokeAsync
确保任务在 UI 线程上 运行。它使用RenderHandle
. 上提供的 - 当渲染器检查参数更改时,任何对象都被认为是脏的,因为渲染器没有简单的方法来检查相等性。
- 渲染器仅在获得线程时间时才为其队列服务。如果您 运行 有一长串同步代码,比如一个按钮单击处理程序,在同步代码完成之前什么也不会发生。
Dispatcher
如果您想进一步挖掘,请深入研究 ComponentBase
- You can view the code here。