谁能解释一下这个话题的基础?
要了解更新过程,您需要了解组件。我会尽量保持简短!
所有组件都必须实现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);
Renderer 管理渲染过程。它包含表示为 RenderTree 的 DOM(由浏览器渲染的内容)。当 Renderer 将一个组件附加到 RenderTree 时,Renderer 会创建一个 RenderHandle 并通过调用 Attach 将其传递给组件。组件使用这个RenderHandle 与渲染器进行通信。 Renderer 通过调用SetParametersAsync 与组件进行通信。
通过在RenderHandle 上调用Render 方法并传递RenderFragment 委托来“渲染”组件。
这是一个简单的渲染片段:
protected RenderFragment HelloWorld => (RenderTreeBuilder builder) =>
{
builder.OpenElement(0, "div");
builder.AddContent(1, "Hello Blazor");
builder.CloseElement();
};
在RenderHandle 上调用Render 不会呈现组件。它只是将渲染片段放在渲染器的队列中。当渲染器运行片段时,它会检查其他组件引用的组件参数更改。它在其参考参数已更改的任何组件上调用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。