Blazor من الصفر: الفصل 3 — مكوّنات قابلة للتوسّع
مرحبًا بك في الفصل 3 من Blazor من الصفر. إذا فاتك الفصل 2، ابدأ به أولًا حتى يكون مشروعك الأساسي جاهزًا.
في الفصل 2 جعلنا التطبيق يعمل. في هذا الفصل سنجعل الكود قابلاً للصيانة.
مشاريع Blazor تصبح فوضوية بسرعة عندما تتحول كل صفحة إلى ملف .razor ضخم. المكوّنات هي الحل: اتساق أعلى، إعادة استخدام أفضل، وحدود أوضح بين أجزاء الواجهة.
ما هو مكوّن Blazor فعليًا؟
المكوّن هو ملف .razor يستطيع:
- عرض الـ markup
- حفظ حالة محلية
- استقبال مدخلات عبر Parameters
- إرسال أحداث إلى المكوّن الأب
- عرض محتوى فرعي
أثناء التشغيل، يتعامل Blazor مع كل مكوّن كآلة حالة صغيرة. عند تغيّر الحالة يعيد التصيير ويطبّق DOM diff.
لذلك الهدف هو تصميم مدخلات واضحة وسلوك متوقّع.
الخطوة 1: ابدأ بمكوّن صغير ومحدد
أنشئ Components/Common/SectionHeader.razor:
<header class="section-header">
<h2>@Title</h2>
@if (!string.IsNullOrWhiteSpace(Subtitle))
{
<p>@Subtitle</p>
}
</header>
@code {
[Parameter] public string Title { get; set; } = string.Empty;
[Parameter] public string? Subtitle { get; set; }
}
استخدمه في Components/Pages/Home.razor:
@page "/"
<PageTitle>Blazor من الصفر</PageTitle>
<SectionHeader
Title="Blazor من الصفر"
Subtitle="الفصل 3 عن المكوّنات." />
المثال بسيط، لكن الفكرة محورية: يجب أن يُفهم المكوّن من خلال معاملاته.
الخطوة 2: اجعل السلوك صريحًا بالمعاملات
لننشئ زرًا قابلاً لإعادة الاستخدام.
Components/Common/AppButton.razor:
<button class="app-button @VariantCssClass" @onclick="OnClick">
@Text
</button>
@code {
[Parameter] public string Text { get; set; } = "Button";
[Parameter] public string Variant { get; set; } = "primary";
[Parameter] public EventCallback OnClick { get; set; }
private string VariantCssClass => Variant.ToLowerInvariant() switch
{
"secondary" => "app-button--secondary",
"danger" => "app-button--danger",
_ => "app-button--primary"
};
}
الاستخدام:
@code {
private int _savedCount;
private void Save()
{
_savedCount++;
}
}
<AppButton Text="Save" Variant="primary" OnClick="Save" />
<p>تم الحفظ @_savedCount مرة.</p>
المهم هنا هو تصميم العقد:
- معاملات قليلة وواضحة
- أسماء صريحة بدل سلوك “سحري”
- قيم افتراضية آمنة
الخطوة 3: استخدم RenderFragment للتركيب
RenderFragment يسمح للمكوّن الأب بتمرير أجزاء واجهة إلى المكوّن الابن.
أنشئ Components/Common/Card.razor:
<article class="card">
<header class="card__header">@Title</header>
<section class="card__body">
@ChildContent
</section>
</article>
@code {
[Parameter] public string Title { get; set; } = string.Empty;
[Parameter] public RenderFragment? ChildContent { get; set; }
}
الاستخدام:
<Card Title="Roadmap">
<ul>
<li>المكوّنات</li>
<li>Data binding</li>
<li>Routing</li>
</ul>
</Card>
هذا النمط يساعدك على بناء صفحات متناسقة بدون تكرار نفس wrapper markup.
الخطوة 4: فضّل التركيب على الصفحات العملاقة
عندما تكبر الصفحة، قسّمها حسب المسؤولية:
ProfileSummaryلكتلة التعريفProfileStatsللمؤشراتProfileActivityListللنشاط الأخير
بهذا تصبح الصفحة طبقة تنسيق، وليست مكانًا لكل التفاصيل.
الخطوة 5: وازن بين الـ markup والمنطق
في المكوّنات البسيطة، @code داخل الملف كافٍ.
في المكوّنات الكبيرة، انقل المنطق إلى code-behind:
UserCard.razorUserCard.razor.cs
هذا يجعل كلًا من الواجهة ومنطق C# أسهل قراءة.
الخطوة 6: بنية مجلدات عملية
بنية فعالة مع النمو:
Components/Pages/-> صفحات قابلة للتوجيهComponents/Layout/-> هيكل التطبيق والتنقّلComponents/Common/-> عناصر عامة مشتركةComponents/Features/<FeatureName>/-> مكوّنات خاصة بكل ميزة
أخطاء شائعة في بدايات مشاريع Blazor
- تمرير عدد كبير من المعاملات بدل نموذج مخصص
- وضع قواعد العمل داخل مكوّنات الصفحات
- إنشاء “مكوّن إلهي” بعدد هائل من الأسطر
- تكرار markup بدل استخراج مكوّنات صغيرة قابلة لإعادة الاستخدام
الفصل القادم
في الفصل 4 سنركّز على Data binding والأحداث: @bind، التعامل مع الأحداث، مفاضلات two-way binding، وأنماط تبقي الحالة متوقعة.