دورة حياة مكون Blazor: الدليل الكامل
أنا أستخدم Blazor منذ فترة وبصراحة، لقد أربكتني أساليب دورة الحياة في البداية. OnInitialized ضد OnInitializedAsync؟ OnParametersSet ضد OnAfterRender؟ متى يقوم StateHasChanged فعليًا بتشغيل إعادة العرض؟ وبعد الكثير من التجربة والخطأ، أصبح لدي أخيرًا نموذج عقلي قوي لكل ذلك.
دورة الحياة في لمحة
عندما يتم عرض مكون Blazor، فإنه يمر عبر هذه الطرق بالترتيب:
SetParametersAsyncOnInitialized/OnInitializedAsyncOnParametersSet/OnParametersSetAsyncOnAfterRender/OnAfterRenderAsyncDispose/DisposeAsync
دعونا نسير من خلال كل واحد.
#SetParametersAsync
هذه هي الطريقة الأولى التي تسمى. يتلقى ParameterView الخام من المكون الأصلي. في معظم الأوقات، لا تتجاوز هذا — حيث يتعامل Blazor مع معلمات التعيين تلقائيًا. ولكن إذا كنت بحاجة إلى معالجة المعلمات المخصصة أو التحقق من صحتها قبل تعيينها:
public override async Task SetParametersAsync(ParameterView parameters)
{
// Custom logic before parameters are set
if (parameters.TryGetValue<string>("Title", out var title))
{
Console.WriteLine($"Title is being set to: {title}");
}
await base.SetParametersAsync(parameters);
}
لقد استخدمت هذا مرة واحدة بالضبط في مشروع حقيقي. في معظم الأحيان سوف تخطي ذلك.
OnInitialized / OnInitializedAsync
هذا هو المكان الذي تقوم فيه بعمل الإعداد - تحميل البيانات، وتهيئة الخدمات، وتعيين القيم الافتراضية. يتم تشغيله مرة واحدة عند إنشاء المكون لأول مرة.
@code {
private List<Product> products = new();
protected override async Task OnInitializedAsync()
{
products = await Http.GetFromJsonAsync<List<Product>>("api/products");
}
}
الشيء الوحيد الذي أزعجني: في Blazor Server، يتم استدعاء OnInitializedAsync مرتين أثناء العرض المسبق. المرة الأولى أثناء العرض المسبق من جانب الخادم، والمرة الثانية عند إنشاء اتصال SignalR. إذا كان استدعاء API الخاص بك مكلفًا، فقد ترغب في التعامل مع ذلك:
private bool isPrerendering = true;
protected override async Task OnInitializedAsync()
{
products = await Http.GetFromJsonAsync<List<Product>>("api/products");
isPrerendering = false;
}
أو من الأفضل استخدام PersistentComponentState لتجنب المكالمة المزدوجة تمامًا.
OnParametersSet / OnParametersSetAsync
يتم تشغيل هذا في كل مرة يقوم فيها المكون الأصلي بإعادة العرض وتمرير قيم المعلمات الجديدة. يتم إطلاقه أيضًا بعد OnInitialized. هذا هو المكان المناسب للرد على تغييرات المعلمات:
[Parameter]
public int CategoryId { get; set; }
private int previousCategoryId;
private List<Product> products = new();
protected override async Task OnParametersSetAsync()
{
if (CategoryId != previousCategoryId)
{
previousCategoryId = CategoryId;
products = await Http.GetFromJsonAsync<List<Product>>(
$"api/products?category={CategoryId}");
}
}
يعد التحقق من CategoryId != previousCategoryId أمرًا مهمًا — فبدونه، يمكنك إعادة تحميل البيانات في كل مرة يقوم فيها الأصل بإعادة العرض، حتى لو لم تتغير الفئة.
OnAfterRender / OnAfterRenderAsync
يتم تشغيل هذا بعد تقديم المكون إلى DOM. تخبرك المعلمة firstRender ما إذا كان هذا هو العرض الأولي. هذا هو المكان المناسب لاستدعاءات JS Interop نظرًا لوجود عناصر DOM في هذه المرحلة:
[Inject]
private IJSRuntime JS { get; set; }
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
await JS.InvokeVoidAsync("initializeChart", chartElement);
}
}
أستخدم firstRender كثيرًا لتجنب إعادة تهيئة مكتبات JavaScript في كل عملية إعادة عرض. إذا كنت تقوم بإعداد مستمعي الأحداث، أو مكتبات المخططات، أو أي شيء يلامس DOM مباشرة، فهذا هو المكان الذي تذهب إليه.
ينبغي تقديم
هذا أقل شهرة ولكنه مفيد للغاية. يتحكم في ما إذا كان المكون يُعاد عرضه عند استدعاء StateHasChanged:
private bool shouldRender = true;
protected override bool ShouldRender() => shouldRender;
private void HeavyOperation()
{
shouldRender = false;
// Do a bunch of state changes without triggering renders
for (int i = 0; i < 1000; i++)
{
items[i].Process();
}
shouldRender = true;
StateHasChanged(); // Now render once with all changes
}
لقد استخدمت هذا لتحسين المكونات التي تعالج القوائم الكبيرة. بدلاً من إعادة العرض عند كل تغيير في العنصر، يمكنك تجميع التحديثات وعرضها مرة واحدة في النهاية.
التخلص / التخلص من Async
عند إزالة مكون من واجهة المستخدم، يجب عليك تنظيف أي موارد. تنفيذ IDisposable أو IAsyncDisposable:
@implements IAsyncDisposable
@code {
private Timer? timer;
private IJSObjectReference? jsModule;
protected override void OnInitialized()
{
timer = new Timer(OnTick, null, 0, 1000);
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
jsModule = await JS.InvokeAsync<IJSObjectReference>("import", "./timer.js");
}
}
public async ValueTask DisposeAsync()
{
timer?.Dispose();
if (jsModule is not null)
{
await jsModule.DisposeAsync();
}
}
}
الأشياء الشائعة التي يجب التخلص منها: المؤقتات، واشتراكات الأحداث، ومراجع وحدة JS، وCancellationTokenSource، وأي خدمات IDisposable أنشأتها.
#تغيرت الحالةهذه ليست طريقة دورة حياة، ولكنها مرتبطة ارتباطًا وثيقًا. يقول لـ Blazor “مرحبًا، لقد تغيرت حالتي، من فضلك أعد عرضي.” يستدعيه Blazor تلقائيًا بعد معالجات الأحداث، لكن في بعض الأحيان تحتاج إلى استدعائه يدويًا — عادةً عندما تتغير الحالة من خارج تدفق أحداث Blazor العادي:
private async Task StartPolling()
{
while (!cts.Token.IsCancellationRequested)
{
data = await Http.GetFromJsonAsync<Data>("api/data");
StateHasChanged(); // Manual call needed since this isn't a Blazor event
await Task.Delay(5000, cts.Token);
}
}
ملاحظة مهمة: إذا كنت تقوم بالتحديث من سلسلة محادثات في الخلفية في Blazor Server، فاستخدم InvokeAsync:
await InvokeAsync(() =>
{
data = newData;
StateHasChanged();
});
#الصورة كاملة
إليك الترتيب الذي يحدث فيه كل شيء:
Component created
└─ SetParametersAsync
└─ OnInitialized / OnInitializedAsync
└─ OnParametersSet / OnParametersSetAsync
└─ Render
└─ OnAfterRender(firstRender: true)
Parameter change from parent
└─ SetParametersAsync
└─ OnParametersSet / OnParametersSetAsync
└─ ShouldRender?
└─ Render
└─ OnAfterRender(firstRender: false)
Component removed
└─ Dispose / DisposeAsync
بمجرد أن يكون لديك هذا التدفق في رأسك، يصبح تصحيح مشكلات دورة الحياة أسهل كثيرًا.
آمل أن تكونوا قد أحببت هذا المنصب! لا تتردد في الاتصال بي على أي وسائل التواصل الاجتماعي على **@emimontesdeoca **.