admin 管理员组

文章数量: 1087817

Blazor组件的生命周期

组件,英文称之为Component,包括普通页面,布局页,用户控件等.

直接看官方文档, Blazor组件的生命周期按从前往后分为以下几个阶段:

  1. SetParametersAsync,接受到参数,初始化之前。
  2. OnInitialized,初始化之后。
  3. OnInitializedAsync,初始化之后(需要处理异步方法的时候用到)。
  4. OnParametersSet,设置完参数之后。
  5. OnParametersSetAsync,设置完参数之后(需要处理异步方法的时候用到)。
  6. OnAfterRender,组件呈现之后。
  7. OnAfterRenderAsync,组件呈现之后(需要处理异步方法的时候用到)。

对比之前的WebForm,这个显然已经算很少了,而且还分成了同步和异步两种操作。不过很奇怪的是为什么参数的处理要分别放在初始化的前后,直观理解的话,OnParametersSet和OnParametersSetAsync这两个事件是可以去掉的。当然框架肯定有她自己的考虑,对于笔者这个Blazor初学者来说肯定有考虑不到的地方。

所有的事件也可以在ComponentBase里面找到定义,这个也是所有组件的基类:

值得注意的是,这里面并没有Dispose方法,根据文档的介绍,需要单独使用@implements指令来声明。

@using System
@implements IDisposable
...
@code {public void Dispose(){...}
}

SetParametersAsync

最早的事件,支持异步操作,她的定义如下:

public virtual Task SetParametersAsync(ParameterView parameters);

ParameterView包含了所有的参数,主要有URL里面的参数以及父组件通过属性传递过来的参数,可以通过foreach遍历里面的值,也可以通过GetValueOrDefault方法根据参数名取值,比如你可以这样:

    public override async Task SetParametersAsync(ParameterView parameters){foreach (var p in parameters){Console.WriteLine(p.Name + ":" + p.Value);}await base.SetParametersAsync(parameters);}

关于base.SetParametersAsync,文档上是这样写的:

If base.SetParametersAync isn’t invoked, the custom code can interpret the incoming parameters value in any way required. For example, there’s no requirement to assign the incoming parameters to the properties on the class.

千万别以为你可以不调用base.SetParametersAync方法,笔者试着把她去调,结果该组件并没有呈现出来。

有没有办法修改参数的值或者增加新的参数呢? 答案是肯定的,可以通过ParameterView.FromDictionary静态方法来构造一个新参数:

    [Parameter]public string Title { get; set; }[Parameter]public string SubTitle { get; set; }public override async Task SetParametersAsync(ParameterView parameters){var newParam = new Dictionary<string, object>();newParam.Add("Title", parameters.GetValueOrDefault<string>("Title"));newParam.Add("SubTitle", "This is the sub Title");await base.SetParametersAsync(ParameterView.FromDictionary(newParam));}

OnInitialized & OnInitializedAsync

定义如下:

protected virtual void OnInitialized();
protected virtual Task OnInitializedAsync();

这两个事件都是在初始化之后,区别是一个是同步的,另一个是异步的,而且每次异步调用之后又会重新绑定一次页面。其实也好理解,OnInitializedAsync里面一般是用来从Web Api加载数据,加载完之后自然要重新绑定。

我们可以看看Blazor初始项目的FetchData页面,稍微做一下修改:

    protected override void OnInitialized(){Console.WriteLine("OnInitialized");base.OnInitialized();}protected override async Task OnInitializedAsync(){Console.WriteLine("OnInitializedAsync");forecasts = await Http.GetFromJsonAsync<WeatherForecast[]>("WeatherForecast");}protected override void OnAfterRender(bool firstRender){Console.WriteLine("OnAfterRender:" + firstRender);base.OnAfterRender(firstRender);}

从控制台我们可以看到她的输出信息如下:

  • OnInitialized
  • OnInitializedAsync
  • OnAfterRender:True
  • OnAfterRender:False

其中OnAfterRender执行了两次,第一次的参数是True(同步线程呈现页面),第二次是False(异步线程加载数据之后重新呈现页面)。我们可以通过这个参数来判断是不是异步加载之后的调用。

OnParametersSet & OnParametersSetAsync

还是一样,先看定义:

protected virtual void OnParametersSet();
protected virtual Task OnParametersSetAsync()

设置参数之后触发, 该事件在OnInitialized之后,但是该事件可以多次触发,拿初始项目的SurveyPrompt组件为例,做一下修改,加上日志:

    protected override void OnInitialized(){Console.WriteLine("OnInitialized");base.OnInitialized();}protected override void OnParametersSet(){Console.WriteLine("OnParametersSet");base.OnParametersSet();}protected override void OnAfterRender(bool firstRender){Console.WriteLine("OnAfterRender:" + firstRender);base.OnAfterRender(firstRender);}

然后修改调用的页面:

<SurveyPrompt Title="@Title" /><button class="btn btn-primary" @οnclick="SetTitle1">Set Title 1
</button>
<button class="btn btn-primary" @οnclick="SetTitle2">Set Title 2
</button>
@code {private string Title { get; set; } = "Default Title";private void SetTitle1(){this.Title = "Title1";}private void SetTitle2(){this.Title = "Title2";}
}

启动的时候控制台输出以下信息:

  • OnInitialized
  • OnParametersSet
  • OnAfterRender:True

当点击按钮Set Title的时候,控制台输出如下:

  • OnParametersSet
  • OnAfterRender:False

如果重复点击同一个Set Title按钮,控制台没有任何输出。

这个很好的解释了我前面的疑问,OnParametersSet并不是可有可无的,她在第一次加载组件的时候会触发,加载之后如果参数发生了改变也会再次触发。

但是如果加上了OnInitializedAsync,情况又不一样了,如下:

    protected override async Task OnInitializedAsync(){Console.WriteLine("OnInitializedAsync Begin");await Task.Run(() =>{System.Threading.Thread.Sleep(3000);Console.WriteLine("OnInitializedAsync End");});await base.OnInitializedAsync();}

控制台输出会变成:

  • OnInitialized
  • OnInitializedAsync Begin
  • OnAfterRender:True
  • OnInitializedAsync End
  • OnParametersSet
  • OnAfterRender:False

OnParametersSet 只触发了一次,而且是在OnInitializedAsync的异步线程结束之后。

OnAfterRender & OnAfterRenderAsync

定义如下:

protected virtual void OnAfterRender(bool firstRender);
protected virtual Task OnAfterRenderAsync(bool firstRender);

组件呈现之后触发,通常用来做JavaScript的互操作,需要注意的是要加上对firstRender参数的判断。因为你所有的自定义函数调用都会重新触发该事件。以FetchData页面为例,假如你加上Refresh函数:

<button class="btn btn-primary" @οnclick="RefreshData">Refresh 
</button>
@code {private async Task RefreshData(){forecasts = await Http.GetFromJsonAsync<WeatherForecast[]>("WeatherForecast");}
}

每次点击按钮的时候都会先触发一次OnAfterRender(Async),通过API拿完数据之后又会触发一次OnAfterRender(Async),两次的firstRender参数都为false。

其他

Dispose

移除组件时触发,前面也说了ComponentBase里面并没有该函数的定义。至于为什么要单独抽出来,其实笔者也不清楚。我们通常在该函数里面进行一些特定资源的释放。

ShouldRender

如果需要阻止UI刷新,可以覆写该方法。

StateHasChanged

通知UI,状态已经更改,以便UI适时刷新。

本文标签: Blazor组件的生命周期