بلیزر هیبرید در .NET MAUI: راهنمای کامل ساخت اپلیکیشن با BlazorWebView در ۲۰۲۶

راهنمای عملی ساخت اپلیکیشن موبایل با Blazor Hybrid روی .NET MAUI در ۲۰۲۶: از تنظیم پروژه با dotnet new، دسترسی به دوربین و SecureStorage، تا اشتراک کد با وب و بهینه‌سازی عملکرد.

راهنمای Blazor Hybrid در .NET MAUI (2026)

به‌روزرسانی: ۷ ژوئیه ۲۰۲۶

بلیزر هیبرید در .NET MAUI یک الگوی معماری است که به شما اجازه می‌دهد کامپوننت‌های Blazor Razor را داخل یک اپلیکیشن native با استفاده از کنترل BlazorWebView اجرا کنید — نتیجه، یک باینری واقعی iOS/Android/Windows/Mac است که UI آن با HTML و C# نوشته شده اما به API‌های سیستم‌عامل و لایه ذخیره‌سازی محلی دسترسی کامل دارد. راستش، در ۲۰۲۶ و با انتشار .NET 10 این رویکرد به یکی از سریع‌ترین راه‌ها برای اشتراک کد بین وب و موبایل تبدیل شده، و ما در چند پروژه اخیر تیم زمان ارائه ویژگی را به میزان قابل توجهی کاهش داده‌ایم. من خودم در آخرین پروژه‌ای که این الگو را روی iOS و Android بردیم، ظرف سه هفته یک PWA موجود را به یک اپ فروشگاهی تبدیل کردم که ۹۰٪ کد UI مشترک داشت.

  • بلیزر هیبرید از کنترل BlazorWebView برای رندر کامپوننت‌های Razor داخل یک WebView میزبان native استفاده می‌کند، اما تفسیر C# روی زمان اجرای .NET native دستگاه انجام می‌شود، نه در مرورگر.
  • بر خلاف Blazor WebAssembly، اینجا هیچ دانلود WASM ندارید. کد C# به صورت native اجرا می‌شود و دسترسی مستقیم به API‌های MAUI Essentials مانند دوربین، ژئولوکیشن و SecureStorage دارید.
  • می‌توانید یک کتابخانه Razor Class Library (RCL) بسازید و همان کامپوننت را در Blazor Server، Blazor WebAssembly و MAUI Blazor Hybrid بازاستفاده کنید. این بزرگ‌ترین برد اقتصادی این الگو است.
  • Startup time و memory footprint در Blazor Hybrid بیشتر از MAUI XAML خالص است، بنابراین برای اپلیکیشن‌های سبک یا صفحاتی که نیاز به رندر شصت‌فریمی دارند، این الگو انتخاب اول ما نیست.
  • در .NET 10 پشتیبانی از Static Web Assets، بارگذاری sourcemap‌ها در حالت debug و JavaScript interop سریع‌تر شده و اکثر مسائل تولیدی نسخه‌های قبلی برطرف شده‌اند.

بلیزر هیبرید در .NET MAUI دقیقاً چیست؟

بلیزر هیبرید یک تکنیک میزبانی است که در آن ما به جای طراحی صفحات با XAML، از کامپوننت‌های Razor استفاده می‌کنیم اما اپلیکیشن نهایی همچنان یک بسته native (IPA برای iOS، APK/AAB برای Android، MSIX برای Windows) است. کنترل BlazorWebView که در پکیج Microsoft.AspNetCore.Components.WebView.Maui ارائه می‌شود، یک نمونه از WebView سیستمی (WKWebView در iOS، WebView2 در Windows، WebView در Android) را ایجاد می‌کند و روی آن یک آدرس مجازی مانند 0.0.0.0/ برای بارگذاری فایل‌های استاتیک از داخل بسته اپلیکیشن ثبت می‌کند.

نکته‌ای که در تیم ما همیشه سر جلسات معماری تکرار می‌شود این است: در Blazor Hybrid، کد C# به صورت native روی زمان اجرای .NET که در همان فرآیند اپلیکیشن اجرا می‌شود اجرا می‌گردد. این با Blazor WebAssembly که در آن C# داخل موتور Wasm مرورگر تفسیر می‌شود، تفاوت بنیادی دارد. عملاً وقتی کاربر روی یک دکمه در کامپوننت Razor کلیک می‌کند، درخواست از طریق یک کانال IPC از WebView به میزبان MAUI منتقل می‌شود، منطق روی رشته UI اپلیکیشن اجرا می‌شود، و سپس تفاوت DOM محاسبه شده و به WebView برای رندر برگردانده می‌شود. این معماری اجازه می‌دهد بدون هیچ فراخوانی شبکه، به کلاس‌های FileSystem, SecureStorage, Preferences, و Connectivity از داخل کامپوننت‌های وب دسترسی داشته باشید، چیزی که در وب خالص هرگز ممکن نیست.

مقایسه Blazor Hybrid با WebAssembly و MAUI خالص

یکی از رایج‌ترین سوالاتی که مدیران محصول از ما می‌پرسند این است که «چه زمانی Blazor Hybrid را انتخاب کنیم و چه زمانی MAUI XAML یا Blazor WebAssembly را؟» جواب کوتاه: به میزان اشتراک کد لازم با وب و به نیازهای عملکرد UI بستگی دارد. جدول زیر خلاصه‌ای است که ما در ارزیابی معماری استفاده می‌کنیم:

ویژگیMAUI XAMLMAUI Blazor HybridBlazor WebAssembly
زبان UIXAML + C#Razor + C#Razor + C#
نصبApp Store / Play StoreApp Store / Play Storeمرورگر (PWA)
دسترسی nativeکاملکاملمحدود (فقط web API‌ها)
اشتراک کد UI با وبخیربله (از طریق RCL)بله
زمان راه‌اندازی سردکمترینمتوسط تا زیادزیاد (دانلود Wasm)
حجم بسته اپحدود ۱۵–۲۵ مگابایتحدود ۲۵–۴۰ مگابایتN/A
عملکرد رندرnative ۶۰fpsWebView (متوسط)WebView (متوسط)
مناسب برایاپ‌های سنگین با UI پیچیدهفرم‌محور، بک‌آفیس، اپ‌های داخلیسایت وب و PWA

در تجربه تیم ما، Blazor Hybrid وقتی مؤثرتر است که تیم شما یک frontend وب موجود با Blazor دارد و می‌خواهید همان کد را در یک اپ موبایل داخلی، اپ فروشگاهی، یا کیوسک ارائه دهید. برای اپ مصرف‌کننده عمومی که در آن experience UI ۶۰fps ملاک قضاوت کاربر است (مثل شبکه‌های اجتماعی، بازی، یا اپ‌های streaming)، MAUI XAML خالص یا حتی همان‌طور که در راهنمای مهاجرت Xamarin.Forms به .NET MAUI بحث کردیم، رویکرد native هنوز انتخاب بهتری است.

ساخت پروژه اول: از dotnet new تا اجرا

در .NET 10 شروع یک پروژه Blazor Hybrid روی MAUI با یک قالب رسمی امکان‌پذیر است. اگر workload‌های maui و maui-blazor روی سیستم شما نصب باشند، مراحل زیر یک اپ کارکردی بدون هیچ تنظیم دستی می‌سازد:

dotnet workload install maui
dotnet workload install maui-blazor

dotnet new maui-blazor -n MobileTechLead.Demo
cd MobileTechLead.Demo

dotnet build -f net10.0-android
dotnet build -f net10.0-ios

ساختار پروژه‌ای که تولید می‌شود کمی متفاوت از یک اپ MAUI عادی است. فایل MauiProgram.cs اکنون AddMauiBlazorWebView() را روی سرویس‌ها فراخوانی می‌کند و یک صفحه اصلی به نام MainPage.xaml وجود دارد که فقط یک BlazorWebView را میزبانی می‌کند و به Main.razor اشاره دارد. تمام صفحات، layout و کامپوننت‌ها زیر پوشه Components/ قرار می‌گیرند. فایل wwwroot/index.html نقطه شروع HTML است، همان‌جا CSS ریشه و تگ‌های meta برای viewport تنظیم می‌شوند.

// MauiProgram.cs: بخش سرویس‌ها
public static MauiApp CreateMauiApp()
{
    var builder = MauiApp.CreateBuilder();
    builder
        .UseMauiApp<App>()
        .ConfigureFonts(fonts =>
        {
            fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
        });

    // ثبت سرویس‌های Blazor Hybrid
    builder.Services.AddMauiBlazorWebView();

#if DEBUG
    builder.Services.AddBlazorWebViewDeveloperTools();
    builder.Logging.AddDebug();
#endif

    // سرویس‌های اپلیکیشن: همان الگوی تزریق وابستگی MVVM
    builder.Services.AddSingleton<IDeviceInfoService, DeviceInfoService>();
    builder.Services.AddScoped<IPreferencesRepository, PreferencesRepository>();

    return builder.Build();
}

کد بالا یکی از مهم‌ترین قسمت‌های تنظیم است. تمام سرویس‌هایی که در کامپوننت‌های Blazor نیاز خواهید داشت باید در همین container ثبت شوند. الگوی تزریق وابستگی و ثبت سرویس کاملاً مشابه چیزی است که در راهنمای معماری MVVM در .NET MAUI برای اپ‌های XAML توضیح دادیم، با این تفاوت که اینجا از [Inject] در Razor به جای constructor injection در ViewModel استفاده می‌شود.

BlazorWebView چگونه کار می‌کند؟

اگر می‌خواهید Blazor Hybrid را در تولید مستقر کنید، درک لایه‌های داخلی BlazorWebView ضروری است. این کنترل در واقع سه لایه دارد: یک WebView سیستمی، یک IPC channel (که Microsoft آن را message bridge می‌نامد) و یک میزبان Razor. جریان کار به این صورت است: وقتی اپ راه‌اندازی می‌شود، BlazorWebView یک URL داخلی مانند https://0.0.0.0/ را در WebView بارگذاری می‌کند. این URL توسط یک StaticContentProvider سرو می‌شود که فایل‌های استاتیک را از داخل بسته اپلیکیشن (نه از دیسک) پاسخ می‌دهد. سپس یک اسکریپت به نام blazor.webview.js اجرا می‌شود که کانال IPC را باز می‌کند.

در تمام تعاملات کاربر، DOM event در WebView رخ می‌دهد، این event به فرمت JSON serialize می‌شود، از طریق کانال IPC به لایه native منتقل می‌شود، در آنجا توسط Razor Renderer پردازش می‌شود (کامپوننت رندر مجدد می‌شود، state ذخیره می‌شود)، تفاوت DOM محاسبه می‌شود، و به صورت یک پیام معکوس به WebView برای اعمال بازگردانده می‌شود. این چرخه معمولاً کمتر از ۱۶ میلی‌ثانیه طول می‌کشد اگر منطق شما سبک باشد، اما هرگونه بلاک روی رشته UI می‌تواند رندر را متوقف کند. این نکته را در بخش بهینه‌سازی عملکرد بیشتر باز خواهیم کرد.

تنظیم HostAddress و آدرس محلی

در برخی نسخه‌های Android قدیمی، آدرس 0.0.0.0/ ممکن است با تنظیمات network security policy تداخل کند. راه‌حل پیشنهادی تیم MAUI این است که آدرس میزبان را به یک مقدار مشخص مانند appassets.mobiletechlead.com تغییر دهید:

<BlazorWebView x:Name="blazorWebView"
               HostPage="wwwroot/index.html"
               HostAddress="appassets.mobiletechlead.com">
    <BlazorWebView.RootComponents>
        <RootComponent Selector="#app" ComponentType="{x:Type components:Routes}" />
    </BlazorWebView.RootComponents>
</BlazorWebView>

دسترسی به API‌های native از کامپوننت‌های Blazor

مهم‌ترین برد Blazor Hybrid این است که هر کد C# که در MAUI اجرا می‌شود، از داخل کامپوننت Razor هم قابل استفاده است. مثلاً برای گرفتن یک عکس از دوربین، ذخیره در فایل‌سیستم، و نمایش آن در UI:

@page "/photo"
@inject IJSRuntime JS

<h3>گرفتن عکس</h3>
<button class="btn btn-primary" @onclick="CapturePhotoAsync">
    باز کردن دوربین
</button>

@if (photoBase64 is not null)
{
    <img src="data:image/jpeg;base64,@photoBase64" style="max-width: 100%;" />
}

@code {
    private string? photoBase64;

    private async Task CapturePhotoAsync()
    {
        // MediaPicker یک API از MAUI Essentials است
        if (!MediaPicker.Default.IsCaptureSupported)
        {
            await JS.InvokeVoidAsync("alert", "دستگاه از دوربین پشتیبانی نمی‌کند");
            return;
        }

        FileResult? photo = await MediaPicker.Default.CapturePhotoAsync();
        if (photo is null) return;

        // ذخیره در فایل‌سیستم اپ
        string localPath = Path.Combine(FileSystem.CacheDirectory, photo.FileName);
        using (var source = await photo.OpenReadAsync())
        using (var destination = File.OpenWrite(localPath))
            await source.CopyToAsync(destination);

        // بارگذاری برای نمایش داخل تگ img
        byte[] bytes = await File.ReadAllBytesAsync(localPath);
        photoBase64 = Convert.ToBase64String(bytes);
        StateHasChanged();
    }
}

در این مثال، MediaPicker.Default و FileSystem.CacheDirectory هر دو از Microsoft.Maui.Essentials می‌آیند و مستقیماً از داخل کامپوننت Razor فراخوانی می‌شوند. هیچ JS interop لازم نیست چون منطق روی رشته native اجرا می‌شود. اجازه‌های دوربین باید در Platforms/Android/AndroidManifest.xml و Platforms/iOS/Info.plist اعلام شوند، یعنی همان قوانین یک اپ MAUI عادی.

ذخیره امن با SecureStorage

برای ذخیره توکن‌ها یا کلیدهای API، از SecureStorage استفاده می‌کنیم. جزئیات بیشتر درباره ذخیره توکن JWT و رفرش خودکار را در مقاله احراز هویت JWT در .NET MAUI پوشش داده‌ایم. در Blazor Hybrid دقیقاً همان الگو کار می‌کند:

@inject Microsoft.Maui.Storage.ISecureStorage Storage

@code {
    protected override async Task OnInitializedAsync()
    {
        var token = await Storage.GetAsync("auth_token");
        if (string.IsNullOrEmpty(token))
            NavigationManager.NavigateTo("/login");
    }
}

اشتراک کد بین اپ وب و اپ موبایل

الگویی که ما در بیشتر پروژه‌های Blazor Hybrid پیشنهاد می‌دهیم، جدا کردن UI اشتراکی به یک Razor Class Library (RCL) است. سپس این RCL توسط سه پروژه بارگذاری می‌شود: پروژه Blazor Server (برای وب داخلی)، پروژه Blazor WebAssembly (برای وب عمومی)، و پروژه MAUI Blazor Hybrid (برای موبایل). تنها کدی که مخصوص یک پلتفرم است در پروژه اصلی می‌ماند، به‌ویژه سرویس‌های وابسته به فایل‌سیستم یا دسترسی سخت‌افزار.

dotnet new razorclasslib -n SharedComponents -f net10.0
dotnet new blazor -n WebApp -f net10.0
# پروژه MAUI را قبلاً ساخته‌ایم

dotnet add MobileTechLead.Demo reference SharedComponents
dotnet add WebApp reference SharedComponents

در RCL، سرویس‌ها را از طریق interface تعریف می‌کنید نه پیاده‌سازی مشخص. مثلاً یک interface به نام IPlatformStorage که در وب با LocalStorage و در MAUI با SecureStorage پیاده‌سازی می‌شود. هر پروژه پیاده‌سازی خود را در Program.cs ثبت می‌کند و کامپوننت‌ها بدون تغییر کار می‌کنند. این جداسازی روی کاغذ ساده به نظر می‌رسد اما در عمل نیاز به دیسیپلین معماری دارد. تیمی که تازه شروع می‌کند وسوسه می‌شود مستقیماً از داخل کامپوننت به کلاس‌های MAUI رجوع کند، اما این کار قابلیت اشتراک وب را از دست می‌دهد.

بهینه‌سازی عملکرد Blazor Hybrid

سه گلوگاه عمده در Blazor Hybrid تجربه کرده‌ایم: startup time، حجم initial paint، و لگ رشته UI هنگام رندر لیست‌های بزرگ. هر کدام راه‌حل خودشان را دارند. برای startup time، اولین کار فعال‌سازی AOT کامپایل است. در .NET 10 می‌توانید فایل پروژه را چنین تنظیم کنید:

<PropertyGroup Condition="'$(Configuration)' == 'Release'">
    <RunAOTCompilation>true</RunAOTCompilation>
    <PublishTrimmed>true</PublishTrimmed>
    <TrimMode>partial</TrimMode>
    <BlazorEnableCompression>true</BlazorEnableCompression>
</PropertyGroup>

در پروژه‌ای که اخیراً روی iOS بهینه کردیم، فعال‌سازی این تنظیمات زمان cold start را از ۲.۸ ثانیه به ۱.۹ ثانیه رساند — همچنان کندتر از MAUI XAML خالص اما در حد پذیرش کاربر. برای اطلاعات کامل‌تر درباره AOT، NativeAOT و تنظیمات memory pressure در MAUI، مقاله بهینه‌سازی عملکرد .NET MAUI در ۲۰۲۶ را ببینید.

Virtualization در لیست‌ها

اگر لیستی با بیش از ۱۰۰ ردیف نمایش می‌دهید، حتماً از کامپوننت Virtualize Blazor استفاده کنید. بدون این، DOM هزاران node می‌سازد و رشته رندر برای ثانیه‌ها متوقف می‌شود:

<Virtualize Items="products" Context="p" ItemSize="72">
    <div class="product-row">
        <img src="@p.Thumb" />
        <span>@p.Name</span>
        <span>@p.Price.ToString("N0") تومان</span>
    </div>
</Virtualize>

کاهش JS interop

هر فراخوانی InvokeAsync یک هزینه serialization دارد. اگر متوجه شدید یک متد را در حلقه فراخوانی می‌کنید، آن را به یک تابع JS واحد که کل عملیات را یکجا انجام می‌دهد، refactor کنید. در تیم ما این را «rule of one round-trip» می‌نامیم.

دیباگ و ابزارها در ۲۰۲۶

دیباگ Blazor Hybrid با تنظیم AddBlazorWebViewDeveloperTools() در MauiProgram.cs ابزارهای DevTools مرورگر را در WebView فعال می‌کند. در Windows می‌توانید Edge DevTools را با فشردن F12 روی BlazorWebView باز کنید. در iOS از Safari Web Inspector و در Android از Chrome DevTools remote debugging استفاده کنید. Breakpoints روی کد C# در Visual Studio 2026 مستقیماً کار می‌کنند، یک تفاوت بزرگ نسبت به Blazor WebAssembly.

Hot Reload هم برای مارک‌آپ Razor و هم برای CSS پشتیبانی می‌شود. تجربه ما این است که تغییرات CSS تقریباً بی‌تأخیر اعمال می‌شوند اما تغییرات ساختاری در کامپوننت‌ها گاهی نیاز به restart دارند، به‌ویژه اگر state سرویس‌های scoped را دستکاری کرده باشید. برای اطلاعات دقیق درباره سناریوهای پشتیبانی‌شده به مستندات رسمی Microsoft برای Blazor Hybrid روی MAUI و Changelog رسمی .NET MAUI در گیت‌هاب مراجعه کنید.

آیا Blazor Hybrid برای تولید آماده است؟

پاسخ صادقانه من: بله، اما با آگاهی از محدودیت‌ها. ما در تیم چندین اپ داخلی و B2B را با Blazor Hybrid به تولید فرستاده‌ایم و تجربه پایدار بوده است، به‌ویژه اپ‌های فرم‌محور، اپ‌های داشبورد فروش، و اپ‌های داخلی که در آن‌ها دوره ارائه ویژگی مهم‌تر از تجربه ۶۰fps است. اما برای اپ‌های مصرف‌کننده عمومی که ملاک ارزیابی کاربر روانی حرکت و انیمیشن است، MAUI XAML خالص یا مسیر native هنوز انتخاب معقول‌تری است.

نکته دیگری که باید مد نظر داشته باشید محدودیت‌های فروشگاه App Store است. اپل به طور کلی اپ‌های wrapper وب را رد می‌کند اما اپلیکیشن‌های Blazor Hybrid که دارای عملکرد native ملموس هستند (مثلاً از دوربین، push notification، ژئولوکیشن استفاده می‌کنند) معمولاً پذیرفته می‌شوند. توصیه ما این است که در فرم بازبینی، حداقل سه ویژگی native را که در نسخه وب موجود نیست، به روشنی برجسته کنید. برای اطلاعات به‌روز درباره سیاست فروشگاه، به راهنمای بازبینی App Store اپل رجوع کنید.

سوالات متداول

تفاوت Blazor Hybrid با Blazor MAUI چیست؟

«Blazor MAUI» یک نام غیررسمی است که مردم گاهی برای Blazor Hybrid که روی .NET MAUI میزبانی می‌شود به کار می‌برند. اسم رسمی که مستندات Microsoft استفاده می‌کنند Blazor Hybrid است و پیاده‌سازی رسمی آن روی MAUI با پکیج Microsoft.AspNetCore.Components.WebView.Maui ارائه می‌شود.

آیا Blazor Hybrid روی iOS نیاز به اپل دولوپر اکانت دارد؟

بله، دقیقاً مانند هر اپ MAUI دیگری. برای تست روی شبیه‌ساز نیازی نیست، اما برای اجرا روی دستگاه فیزیکی، تست‌فلایت، یا انتشار در App Store به Apple Developer Program با پرداخت سالانه ۹۹ دلار نیاز دارید.

آیا می‌توانم از کامپوننت‌های MudBlazor یا Radzen در Blazor Hybrid استفاده کنم؟

بله. تمام کتابخانه‌های کامپوننت Blazor که به Blazor Server یا WebAssembly سازگارند در Blazor Hybrid هم کار می‌کنند. تنها هشدار این است که برخی از انیمیشن‌های سنگین این کتابخانه‌ها روی دستگاه‌های اندروید ارزان‌قیمت ممکن است لگ داشته باشند و باید انیمیشن‌ها را ساده‌تر کنید.

حجم نهایی اپ Blazor Hybrid چقدر است؟

یک اپ خالی حدود ۲۸ تا ۳۵ مگابایت روی اندروید (APK) و حدود ۲۰ تا ۲۵ مگابایت روی iOS (IPA) است. با فعال کردن AOT و trimming می‌توان این عدد را حدود ۲۰ درصد کاهش داد. حجم افزودنی نسبت به MAUI خالص عمدتاً به دلیل runtime Blazor و فایل‌های استاتیک است.

آیا Blazor Hybrid از offline کامل پشتیبانی می‌کند؟

بله. چون فایل‌های استاتیک داخل بسته اپ قرار دارند و منطق C# روی دستگاه اجرا می‌شود، اپ بدون اینترنت هم کار می‌کند. برای همگام‌سازی داده‌ها با سرور می‌توانید از الگویی مشابه چیزی که در راهنمای معماری Offline-First در .NET MAUI پیاده‌سازی کرده‌ایم استفاده کنید.

Priya Sharma
درباره نویسنده Priya Sharma

Cross-platform engineering lead who's shipped apps to millions on both Play Store and App Store. Believes shared codebases shouldn't mean shared mediocrity.