برنامه نویسی

Host React با سرور Blazor

این مقاله آزمایش من برای میزبانی React را در پروژه Blazor Server بررسی می کند. Blazor یک مدل میزبانی به نام Hosted Blazor ارائه می دهد که سرور Blazor و Blazor WebAssembly (WASM) را در یک راه حل واحد ترکیب می کند. این به برنامه اجازه می دهد تا HTML را با تولید سمت سرور یا سمت مشتری ارائه دهد.

با این حال، React به دلیل اکوسیستم قوی خود تقریباً در همه جنبه ها، به ویژه طیف گسترده ای از انتخاب های کتابخانه UI، برتر است. این مزیت بالقوه انگیزه این آزمایش بود.

پیش نیازها

در اینجا موارد ضروری است که باید دنبال کنید:

  • گره + pnpm (یا npm یا مشابه). این راهنما استفاده می کند pnpm.
  • به سرعت برای راه اندازی React.
  • NET Core (NET 7 یا جدیدتر — که از پروژه Blazor پشتیبانی می کند)

pnpm را نصب کنید

به دستورالعمل نصب رسمی اینجا مراجعه کنید: راهنمای نصب pnpm.

همچنین می توانید Node.js را با استفاده از pnpm دستور:

pnpm env use --global lts
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

React Side را راه اندازی کنید

بیایید با راه اندازی برنامه React بدون قابلیت همکاری دات نت شروع کنیم.

مراحل

  1. یک دایرکتوری برای میزبانی راه حل ایجاد کنید:

    به عنوان مثال، دایرکتوری به نام ایجاد کنید HostedReact.

  2. در آن دایرکتوری، یک راه حل و یک برنامه سرور Blazor جدید ایجاد کنید:

    ما یک فهرست راه حل برای میزبانی دو پروژه خواهیم داشت: یکی پروژه سرور Blazor و دیگری یک پروژه React است.

    دستورات زیر را در ترمینال خود اجرا کنید:

    dotnet new sln
    dotnet new gitignore
    md src
    dotnet new blazor -o ./src/Sample
    dotnet sln add ./src/Sample
    
  3. با استفاده از Vite در یک برنامه React ایجاد کنید src دایرکتوری:

    cd src
    pnpm create vite@latest
    cd <Your-New-Project-Name>
    pnpm install
    
  4. تعاریف نوع گره را برای ایمنی نوع بهتر نصب کنید:

    pnpm add -D @types/node
    
  5. نصب کنید nodePolyfilles برای مدیریت متغیرهای محیطی با Vite:

    Vite از متغیرهای محیطی مانند process برای حل مسیرها، و این ابزارها برای سازگاری ضروری هستند:

    pnpm add node-stdlib-browser
    pnpm add -D vite-plugin-node-stdlib-browser
    
  6. یک نقطه ورودی ماژول برای پروژه React ایجاد کنید تا توسط Blazor مصرف شود:

    یک فایل جدید ایجاد کنید entrypoint.jsx در React src دایرکتوری

    import {createRoot} from "react-dom/client";
    import {StrictMode} from "react";
    import App from "./App.jsx";
    import './index.css'
    
    export function Initialize(el){
        createRoot(el).render(
            <StrictMode>
                <App />
            StrictMode>
        )
    }
    

    اصلاح کنید main.jsx برای استفاده مجدد از این تابع نقطه ورودی.

    import './index.css'
    import {Initialize} from "./entrypoint.jsx";
    
    Initialize(document.getElementById('root'))
    
  7. Vite را برای تولید کتابخانه (به جای یک برنامه وب استاتیک) پیکربندی کنید:

    به‌طور پیش‌فرض، Vite یک برنامه وب استاتیک تولید می‌کند که باندل و index.html و فایل های جاوا اسکریپت لازم با این حال، برای ادغام با پروژه Blazor Server، ما به آن برای خروجی یک ماژول جاوا اسکریپت نیاز داریم. پیکربندی Vite خود را به روز کنید (vite.config.js یا vite.config.ts) به شرح زیر:

    import path from "path"
    import { defineConfig } from 'vite'
    import react from '@vitejs/plugin-react-swc'
    
    // @ts-expect-error Test
    import nodePolyfills from "vite-plugin-node-stdlib-browser"
    
    // https://vite.dev/config/
    export default defineConfig({
    base: "/react/",
    build: {
        lib: {
        entry: path.resolve(__dirname, "src/entrypoint.jsx"),
        name: "ReactApp",
        fileName: "ReactApp",
        formats: ["es"]
        },
        outDir: "../Sample/wwwroot/react",
        emptyOutDir: true
    },
    plugins: [react(), nodePolyfills()],
    resolve: {
        alias: {
        "@": path.resolve(__dirname, "./src")
        }
    }
    })
    

    یادداشت ها:

    • این outDir فیلد محل تولید فایل‌ها را مشخص می‌کند و با استاتیک Blazor هم‌تراز می‌شود wwwroot دایرکتوری
    • این base کلید برای سرور توسعه دهنده Vite است و شامل آن نمی شود wwwroot بخش

    توصیه می کنم از React JavaScript (به جای TypeScript) استفاده کنید تا هنگام فراخوانی متدهای دات نت از JS از هزینه های اضافی جلوگیری کنید. با جاوا اسکریپت، نیازی به اعلان انواع صریح در طول interop نخواهید داشت. با این حال، این اختیاری است و به ترجیح شما بستگی دارد.

  8. ساخت پروژه React:

    دستور زیر را اجرا کنید تا فایل های لازم را در زیر ایجاد کنید wwwroot/react دایرکتوری:

    pnpm run build
    

    خروجی شامل خواهد شد ReactApp.css و ReactApp.js. اطمینان حاصل کنید که اضافه کنید /src/Sample/wwwroot/react به پروژه شما .gitignore.

راه اندازی Blazor Side

اکنون که پروژه React را پیکربندی کرده اید، زمان آن رسیده است که آن را با Blazor ادغام کنید تا بتواند برنامه React را میزبانی کند. این شامل استفاده از Initialize عملکردی که قبلاً در React اعلام شد entrypoint.jsx فایل

مراحل:

  1. طرح Blazor را ساده کنید:

    از آنجایی که برنامه React دارای سبک خاص خود (css) است، ممکن است بخواهیم از شر چیزهای مرتبط با سبک Blazor خلاص شویم. حتی ممکن است یک کامپوننت layout به طور خاص برای هدف رندر React ایجاد کنید.

    به روز رسانی MainLayout.razor به یک الگوی خالی:

    @inherits LayoutComponentBase
    
    @Body
    

    برای App.razor، آن را حداقل نگه دارید:

    
     lang="en">
    
    
         charset="utf-8" />
         name="viewport" content="width=device-width, initial-scale=1.0" />
         href="https://dev.to/" />
         rel="stylesheet" href="@Assets["Sample.styles.css"]" />
         />
         rel="icon" type="image/png" href="favicon.png" />
         />
    
    
    
         />
        "_framework/blazor.web.js">
    
    
    
    
  2. یک کلاس interop JS برای ادغام React ایجاد کنید:

    Blazor از JavaScript Interop برای فراخوانی توابع جاوا اسکریپت و دریافت داده ها از سمت جاوا اسکریپت استفاده می کند. برای رندر کردن اجزای React، از JavaScript Interop برای فراخوانی استفاده می کنیم Initialize تابعی که در خود تعریف کردید entrypoint.jsx فایل

    ایجاد یک ReactJsInterop.cs فایل در ریشه پروژه (یا فضای نام مناسب دیگری). کد زیر را اضافه کنید:

    using Microsoft.AspNetCore.Components;
    using Microsoft.JSInterop;
    
    namespace HostedReact;
    
    public sealed class ReactJsInterop(IJSRuntime js) : IAsyncDisposable
    {
        readonly Lazy<Task<IJSObjectReference>> importModule
            = new(() => js.InvokeAsync<IJSObjectReference>("import", "./react/ReactApp.js").AsTask());
    
        public ValueTask Initialize(ElementReference el)
            => InvokeVoidAsync("Initialize", el);
    
        async ValueTask InvokeVoidAsync(string identifier, params object?[]? args) {
            var module = await importModule.Value;
            await module.InvokeVoidAsync(identifier, args);
        }
    
        async ValueTask<T> InvokeAsync<T>(string identifier, params object?[]? args) {
            var module = await importModule.Value;
            return await module.InvokeAsync<T>(identifier, args);
        }
    
        public async ValueTask DisposeAsync() {
            if (importModule.IsValueCreated){
                using var import = importModule.Value;
                var module = await import;
                await module.DisposeAsync();
            }
        }
    }
    

    یادداشت ها:

    • این کلاس به صورت پویا ماژول جاوا اسکریپت برنامه React را وارد می کند (ReactApp.js) در زمان اجرا و استفاده می کند Initialize عملکردی برای رندر کردن اجزای React در یک عنصر DOM.
    • این ElementReference پارامتر برای عبور عنصر DOM که React UI را تزریق می کند استفاده می شود.

    بعد، اضافه کنید ReactJsInterop سرویس به ظرف تزریق وابستگی (DI) شما. را به روز کنید Program.cs فایل شامل ثبت نام:

    builder.Services.AddScoped<ReactJsInterop>();
    
  3. رندر برنامه React با Interop:

    استفاده کنید ReactJsInterop کلاس در صفحه Razor برای میزبانی اجزای React. بیایید اصلاح کنیم Home.razor به عنوان مثال:

    @page "https://dev.to/"
    
    @rendermode @(new InteractiveServerRenderMode(prerender: false))
    
    @inject ReactJsInterop ReactJs
    
    <link rel="stylesheet" href="./react/ReactApp.css" />
    <div @ref="div" id="root">Loading...div>
    
    @code {
    
        ElementReference div;
    
        protected override async Task OnAfterRenderAsync(bool firstRender) {
            if (firstRender)
                await ReactJs.Initialize(div);
        }
    
    }
    

    یادداشت ها:

    • مرجع را به شیوه نامه React ایجاد شده اضافه کنید ReactApp.css به طوری که استایل های React در Blazor اعمال می شوند.
    • استفاده کنید @ref دستورالعملی برای پیوند یک Blazor کنترل شده است div عنصر (با id="root") برای React تا اجزای خود را رندر کند.
  4. اجرا و تأیید کنید

    برنامه Blazor را با استفاده از dotnet run دستور دهید و به مسیر پیکربندی شده بروید (مثلاً /) در مرورگر شما. باید برنامه React را در DOM برنامه Blazor مشاهده کنید.

    اگر تغییراتی در برنامه React ایجاد شد، باید آن را با استفاده از:

    pnpm run build
    

    این باعث بازسازی مجدد می شود ReactApp.js و ReactApp.css فایل های داخل Blazor wwwroot/react دایرکتوری

قابلیت همکاری دو طرفه را اضافه کنید

تا کنون، ما React را به گونه‌ای پیکربندی کرده‌ایم که در داخل کامپوننت Blazor با استفاده از Initialize تابع در حالی که این راه‌اندازی ارتباط مستقیم را امکان‌پذیر می‌کند – از NET (Blazor) به React – ما می‌توانیم آن را گسترش دهیم تا به React اجازه دهیم به دات‌نت تماس بگیرد و تعامل دوطرفه کامل را ممکن می‌سازد. این با استفاده از جاوا اسکریپت Interop Blazor ([JSInvokable]) در کنار پیشرفت هایی در نقطه ورود React.

این قابلیت همکاری می‌تواند برای سناریوهایی مانند به‌روزرسانی مؤلفه‌های React UI بر اساس داده‌های واکشی شده از یک سرویس Blazor یا راه‌اندازی منطق Blazor از اقدامات کاربر در برنامه React مفید باشد.

مراحل:

  1. Methods را در Blazor برای React to Call در معرض نمایش قرار دهید

    شما باید متدهایی را در Blazor تعریف کنید که React بتواند با استفاده از مکانیسم interop .NET-to-JS فراخوانی کند. بیایید به روز رسانی کنیم Home.razor فایل شامل الف [JSInvokable] روشی که React می تواند فراخوانی کند.

    به روز رسانی Home.razor مانند این:

    @page "https://dev.to/"
    
    @rendermode @(new InteractiveServerRenderMode(prerender: false))
    
    @inject ReactJsInterop ReactJs
    @implements IDisposable
    
    <link rel="stylesheet" href="./react/ReactApp.css" />
    <div @ref="div" id="root">Loading...div>
    
    @code {
    
        ElementReference div;
        readonly DotNetObjectReference<Home> me;
    
        public Home() {
            me = DotNetObjectReference.Create(this);
        }
    
        protected override async Task OnAfterRenderAsync(bool firstRender) {
            if (firstRender)
                await ReactJs.Initialize(div, me);
        }
    
        [JSInvokable]
        public int GetSecretNumber() => 123;
    
        public void Dispose() {
            me.Dispose();
        }
    
    }
    
    

    GetSecretNumber روشی است که از برنامه React فراخوانی می شود. توجه داشته باشید که باید متدهای فراخوانی JS را با علامت گذاری کنیم JSInvokableAttribute. یک روش JS می تواند همگام یا همگام باشد.

    همچنین توجه کنید که IDisposable پیاده سازی برای آزادسازی منابع تخصیص یافته مورد نیاز است DotNetObjectReference، برای جلوگیری از نشت حافظه.

  2. به روز رسانی React Initialize Function:

    در شما entrypoint.jsx فایل، گسترش دهید Initialize تابع پذیرش اضافی .NET مرجع شی از این برای فراخوانی متد دات نت از React استفاده کنید.

    را به روز کنید entrypoint.jsx فایل مانند این:

    import {createRoot} from "react-dom/client";
    import {StrictMode} from "react";
    import App from "./App.jsx";
    import './index.css'
    
    export async function Initialize(el, dotnet){
        const number = await dotnet.invokeMethodAsync("GetSecretNumber")
    
        createRoot(el).render(
            <StrictMode>
                <App n={number} />
            StrictMode>
        )
    }
    

    و بس. اکنون ما بین JS و .NET ارتباط دو طرفه داریم.

نتیجه گیری

این آزمایش برای استفاده از اکوسیستم و تجربه توسعه‌دهنده برتر React، به ویژه ویژگی Hot Module Replacement (HMR) که امکان توسعه سریع‌تر رابط کاربری را فراهم می‌کند، آغاز شد. در حالی که Blazor دارای HMR است، تغییرات در کد دات‌نت یا فایل‌های Razor اغلب نیاز به بازسازی دارند، که باعث می‌شود توسعه در مقایسه با کارایی کمتری داشته باشد.

با این حال، این رویکرد ترکیبی دارای معاوضه هایی است:

  • در حالی که React با HMR بهتر و اکوسیستم غنی آن، توسعه UI را سرعت می بخشد، یک لایه API اضافی بین دات نت و جاوا اسکریپت معرفی می کند که در مقایسه با برنامه های Blazor خالص، پیچیدگی و سربار بالقوه را اضافه می کند.
  • اضافی pnpm run build مرحله برای React 3 تا 4 ثانیه طول می کشد تا کامل شود، که تقریباً با زمان تدوین HMR Blazor قابل مقایسه است و مزایای درک شده در موارد استفاده ساده را به حداقل می رساند.

گفته می‌شود، این راه‌اندازی ارزش خود را در پروژه‌هایی نشان می‌دهد که در آن نقاط قوت React در توسعه UI از قبل با معماری مبتنی بر API موجود، مانند پروژه‌هایی که از HTTP یا gRPC برای ارتباط استفاده می‌کنند، همسو هستند. در چنین مواردی، ادغام React-Blazor می‌تواند با بهره‌گیری از انعطاف‌پذیری React راه‌حلی قدرتمند و منسجم ارائه دهد و در عین حال از قابلیت‌های باطنی/ سمت سرور قوی Blazor بهره می‌برد.

به طور خلاصه، اگرچه این رویکرد ترکیبی ممکن است راه‌حلی برای همه نباشد، اما می‌تواند یک انتخاب قانع‌کننده برای برنامه‌هایی باشد که هم به اکوسیستم UI پویا React و هم به ادغام یکپارچه با محیط Full Stack .NET Blazor نیاز دارند.

نوشته های مشابه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

دکمه بازگشت به بالا