Blazor 组件生命周期实例说明
1.简介
Blazor的生命周期与React组件的生命周期类似,也分为三个阶段:初始化阶段、运行中阶段和销毁阶段,其相关方法有10个,包括设置参数前、初始化、设置参数之后、组件渲染后以及组件的销毁,但是这些方法有些是重复的,只不过是同步与异步的区别。本文直接实例说明 Blazor 的生命周期
Blazor生命周期方法主要包括:
1 设置参数前 SetParametersAsync
2 初始化 OnInitialized/OnInitializedAsync
3 设置参数后 OnParametersSet/OnParametersSetAsync
4 组件渲染呈现后 OnAfterRender/OnAfterRenderAsync
5 判断是否渲染组件 ShouldRender
6 组件删除前 Dispose
7 通知组件渲染 StateHasChanged
需要注意的点:
(1)OnAfterRender/OnAfterRenderAsync方法有一个bool类型的形参firstRender,用于指示是否是第一次渲染(即组件初始化时的渲染)。
(2)同步方法总是先于异步方法执行。
(3)StateHasChanged 强制实现组件刷新。
2.代码实例
https://github.com/densen2014/Blazor100/tree/master/BlazorLifecycle
mkdir BlazorLifecycle
cd BlazorLifecycle
dotnet new blazorserver
3. Index.razor
代码
@page "/"
@page "/{StartDate:datetime}"
@page "/2/{StartDate2:datetime}"
@using Microsoft.Extensions.Logging
@inject ILogger<Index> Logger
<PageTitle>Index</PageTitle>
<pre>@message</pre>
<button @onclick="LogInformation">Log information (and trigger a render)</button>
@code {
private string? message;
protected override void OnInitialized()
{
message += $"{Environment.NewLine}{DateTime.Now:hh:mm:ss.fff} OnInitialized";
}
protected override async Task OnInitializedAsync()
{
message += $"{Environment.NewLine}{DateTime.Now:hh:mm:ss.fff} OnInitializedAsync";
await Task.CompletedTask;
}
[Parameter]
public DateTime StartDate { get; set; }
[Parameter]
public DateTime StartDate2 { get; set; }
protected override void OnParametersSet()
{
if (StartDate == default)
{
StartDate = DateTime.Now;
message += $"{Environment.NewLine}{DateTime.Now:hh:mm:ss.fff} OnParametersSet=>没有设置参数:开始日期。应用默认值 (StartDate: {StartDate}).";
}
else
{
message += $"{Environment.NewLine}{DateTime.Now:hh:mm:ss.fff} OnParametersSet=>设置参数:开始日期 (StartDate: {StartDate}).";
}
}
protected override async Task OnParametersSetAsync()
{
if (StartDate2 == default)
{
StartDate2 = DateTime.Now;
message += $"{Environment.NewLine}{DateTime.Now:hh:mm:ss.fff} OnParametersSetAsync=>没有设置参数:开始日期。应用默认值 (StartDate2: {StartDate2}).";
}
else
{
message += $"{Environment.NewLine}{DateTime.Now:hh:mm:ss.fff} OnParametersSetAsync=>设置参数:开始日期 (StartDate2: {StartDate2}).";
}
await Task.CompletedTask;
}
protected override void OnAfterRender(bool firstRender)
{
if (firstRender)
{
message += $"{Environment.NewLine}{DateTime.Now:hh:mm:ss.fff} OnAfterRender=>为第一次渲染执行.";
StateHasChanged();
}
else
{
message += $"{Environment.NewLine}{DateTime.Now:hh:mm:ss.fff} OnAfterRender=>非第一次渲染.";
}
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
message += $"{Environment.NewLine}{DateTime.Now:hh:mm:ss.fff} OnAfterRenderAsync=>为第一次渲染执行.";
StateHasChanged();
}
else
{
message += $"{Environment.NewLine}{DateTime.Now:hh:mm:ss.fff} OnAfterRenderAsync=>非第一次渲染.";
}
await Task.CompletedTask;
}
private void LogInformation()
{
Logger.LogInformation("LogInformation called");
}
}
4. 执行结果
点击按钮后
5. 子组件 Index1.razor
@page "/Index2"
@page "/Index2/{StartDate:datetime}"
@page "/Index22/{StartDate2:datetime}"
@page "/Index222/{Count:int}"
@using Microsoft.Extensions.Logging
@inject ILogger<Index> Logger
<h3>子组件</h3>
<pre>@message</pre>
<button @onclick="LogInformation">Log information (and trigger a render)</button>
<p>刷新计数器: @refreshTimes</p>
@if (listItem == null)
{
<p>Loading... 模拟加载数据3秒</p>
}
else
{
<p>数据列表</p>
<ui>
@foreach (var item in listItem)
{
<li>@item</li>
}
</ui>
}
@{
refreshTimes++;
}
@code {
private string? message;
private List<string>? listItem;
[Parameter]
public DateTime StartDate { get; set; }
[Parameter]
public DateTime StartDate2 { get; set; }
[Parameter]
public int? Count { get; set; }
private int refreshTimes;
protected override void OnInitialized()
{
message += $"{Environment.NewLine}{DateTime.Now:hh:mm:ss.fff} OnInitialized";
}
protected override async Task OnInitializedAsync()
{
message += $"{Environment.NewLine}{DateTime.Now:hh:mm:ss.fff} OnInitializedAsync";
await Task.CompletedTask;
}
protected override void OnParametersSet()
{
if (StartDate == default)
{
StartDate = DateTime.Now;
message += $"{Environment.NewLine}{DateTime.Now:hh:mm:ss.fff} OnParametersSet=>没有设置参数:开始日期。应用默认值 (StartDate: {StartDate}).";
}
else
{
message += $"{Environment.NewLine}{DateTime.Now:hh:mm:ss.fff} OnParametersSet=>设置参数:开始日期 (StartDate: {StartDate}).";
}
if (Count == default)
{
message += $"{Environment.NewLine}{DateTime.Now:hh:mm:ss.fff} OnParametersSet=>没有设置参数:计数.";
}
else
{
message += $"{Environment.NewLine}{DateTime.Now:hh:mm:ss.fff} OnParametersSet=>设置参数:计数 (Count: {Count}).";
}
}
protected override async Task OnParametersSetAsync()
{
if (StartDate2 == default)
{
StartDate2 = DateTime.Now;
message += $"{Environment.NewLine}{DateTime.Now:hh:mm:ss.fff} OnParametersSetAsync=>没有设置参数:开始日期。应用默认值 (StartDate2: {StartDate2}).";
}
else
{
message += $"{Environment.NewLine}{DateTime.Now:hh:mm:ss.fff} OnParametersSetAsync=>设置参数:开始日期 (StartDate2: {StartDate2}).";
}
if (Count == default)
{
message += $"{Environment.NewLine}{DateTime.Now:hh:mm:ss.fff} OnParametersSetAsync=>没有设置参数:计数.";
}
else
{
message += $"{Environment.NewLine}{DateTime.Now:hh:mm:ss.fff} OnParametersSetAsync=>设置参数:计数 (Count: {Count}).";
listItem = null;
await MockData();
}
await Task.CompletedTask;
}
protected override void OnAfterRender(bool firstRender)
{
if (firstRender)
{
message += $"{Environment.NewLine}{DateTime.Now:hh:mm:ss.fff} OnAfterRender=>为第一次渲染执行.";
StateHasChanged();
}
else
{
message += $"{Environment.NewLine}{DateTime.Now:hh:mm:ss.fff} OnAfterRender=>非第一次渲染.";
}
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
message += $"{Environment.NewLine}{DateTime.Now:hh:mm:ss.fff} OnAfterRenderAsync=>为第一次渲染执行.";
StateHasChanged();
if (Count == null) {
Count = Count ?? 5;
message += $"{Environment.NewLine}{DateTime.Now:hh:mm:ss.fff} OnAfterRenderAsync=>没有设置参数:计数,使用默认值 5.";
await MockData();
}
}
else
{
message += $"{Environment.NewLine}{DateTime.Now:hh:mm:ss.fff} OnAfterRenderAsync=>非第一次渲染.";
}
await Task.CompletedTask;
}
private void LogInformation()
{
Logger.LogInformation("LogInformation called");
}
protected async Task MockData()
{
message += $"{Environment.NewLine}{DateTime.Now:hh:mm:ss.fff} MockData Delay 3s 模拟加载数据";
StateHasChanged();
if (listItem == null)
{
await Task.Delay(3000);
listItem = new List<string>();
for (int i = 0; i < Count; i++)
{
listItem.Add(Guid.NewGuid().ToString());
}
message += $"{Environment.NewLine}{DateTime.Now:hh:mm:ss.fff} MockData 模拟加载数据完成 , 刷新UI";
StateHasChanged();
}
}
}
6. 改造 Index.razor
在最后一行加入代码
<Index1 StartDate="new DateTime (2019,1,1)" Count="count" />
Count :
<InputNumber @bind-Value="@count" />
<button @onclick="SetCount">Set</button>
@code {
private int? count=2;
private void SetCount()
{
Logger.LogInformation("SetCount");
}
}
7.改造 NavMenu.razor
<div class="@NavMenuCssClass nav-scrollable" @onclick="ToggleNavMenu">
<nav class="flex-column">
<div class="nav-item px-3">
<NavLink class="nav-link" href="" Match="NavLinkMatch.All">
<span class="oi oi-home" aria-hidden="true"></span> 生命周期
</NavLink>
</div>
<div class="nav-item px-3">
<NavLink class="nav-link" href="/1999-9-9">
<span class="oi oi-home" aria-hidden="true"></span> 生命周期带参
</NavLink>
</div>
<div class="nav-item px-3">
<NavLink class="nav-link" href="index2">
<span class="oi oi-plus" aria-hidden="true"></span> 子组件
</NavLink>
</div>
<div class="nav-item px-3">
<NavLink class="nav-link" href="index222/10">
<span class="oi oi-plus" aria-hidden="true"></span> 子组件带参
</NavLink>
</div>
<div class="nav-item px-3">
<NavLink class="nav-link" href="Index22/2999-9-9">
<span class="oi oi-plus" aria-hidden="true"></span> 子组件带参2
</NavLink>
</div>
</nav>
</div>
7. 执行结果
首次运行
设置参数10
其他链接
生命周期带参
子组件
子组件带参
子组件带参2
8. 总结
预呈现后的有状态重新连接
在 Blazor Server 应用中,当 RenderMode 为 ServerPrerendered 时,组件最初作为页面的一部分静态呈现。 浏览器重新建立与服务器的 SignalR 连接后,将再次呈现组件,并且该组件为交互式。 如果存在用于初始化组件的 OnInitialized{Async} 生命周期方法,则该方法执行两次:
- 在静态预呈现组件时执行一次。
- 在建立服务器连接后执行一次。
在最终呈现组件时,这可能导致 UI 中显示的数据发生明显变化。 若要避免在 Blazor Server 应用中出现此双重呈现行为,请传递一个标识符以在预呈现期间缓存状态并在预呈现后检索状态。
检查 listItem 为空显示载入中,数据就绪后再刷新页面显示数据
@if (listItem == null)
{
<p>Loading... 模拟加载数据3秒</p>
}
else
{
<p>数据列表</p>
<ui>
@foreach (var item in listItem)
{
<li>@item</li>
}
</ui>
}
设置参数后执行
protected override async Task OnParametersSetAsync()
{
if (Count != default)
{
message += $"{Environment.NewLine}{DateTime.Now:hh:mm:ss.fff} OnParametersSetAsync=>设置参数:计数 (Count: {Count}).";
listItem = null;
await MockData();
}
await Task.CompletedTask;
}
未设置参数异步载入数据
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
if (Count == null) {
Count = Count ?? 5;
message += $"{Environment.NewLine}{DateTime.Now:hh:mm:ss.fff} OnAfterRenderAsync=>没有设置参数:计数,使用默认值 5.";
await MockData();
}
}
await Task.CompletedTask;
}
关联项目
FreeSql QQ群:4336577
BA & Blazor QQ群:795206915
Maui Blazor 中文社区 QQ群:645660665
知识共享许可协议
本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。欢迎转载、使用、重新发布,但务必保留文章署名AlexChow(包含链接: https://github.com/densen2014 ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请与我联系 。
转载声明
本文来自博客园,作者:周创琳 AlexChow,转载请注明原文链接:https://www.cnblogs.com/densen2014/p/17576763.html
AlexChow
今日头条 | 博客园 | 知乎 | Gitee | GitHub
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek-R1本地部署如何选择适合你的版本?看这里
· 传国玉玺易主,ai.com竟然跳转到国产AI
· 自己如何在本地电脑从零搭建DeepSeek!手把手教学,快来看看! (建议收藏)
· 我们是如何解决abp身上的几个痛点
· 普通人也能轻松掌握的20个DeepSeek高频提示词(2025版)