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 بدون قابلیت همکاری دات نت شروع کنیم.
مراحل
-
یک دایرکتوری برای میزبانی راه حل ایجاد کنید:
به عنوان مثال، دایرکتوری به نام ایجاد کنید
HostedReact. -
در آن دایرکتوری، یک راه حل و یک برنامه سرور Blazor جدید ایجاد کنید:
ما یک فهرست راه حل برای میزبانی دو پروژه خواهیم داشت: یکی پروژه سرور Blazor و دیگری یک پروژه React است.
دستورات زیر را در ترمینال خود اجرا کنید:
dotnet new sln dotnet new gitignore md src dotnet new blazor -o ./src/Sample dotnet sln add ./src/Sample -
با استفاده از Vite در یک برنامه React ایجاد کنید
srcدایرکتوری:cd src pnpm create vite@latest cd <Your-New-Project-Name> pnpm install -
تعاریف نوع گره را برای ایمنی نوع بهتر نصب کنید:
pnpm add -D @types/node -
نصب کنید
nodePolyfillesبرای مدیریت متغیرهای محیطی با Vite:Vite از متغیرهای محیطی مانند
processبرای حل مسیرها، و این ابزارها برای سازگاری ضروری هستند:pnpm add node-stdlib-browser pnpm add -D vite-plugin-node-stdlib-browser -
یک نقطه ورودی ماژول برای پروژه React ایجاد کنید تا توسط Blazor مصرف شود:
یک فایل جدید ایجاد کنید
entrypoint.jsxدر Reactsrcدایرکتوری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')) -
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 نخواهید داشت. با این حال، این اختیاری است و به ترجیح شما بستگی دارد.
- این
-
ساخت پروژه React:
دستور زیر را اجرا کنید تا فایل های لازم را در زیر ایجاد کنید
wwwroot/reactدایرکتوری:pnpm run buildخروجی شامل خواهد شد
ReactApp.cssوReactApp.js. اطمینان حاصل کنید که اضافه کنید/src/Sample/wwwroot/reactبه پروژه شما.gitignore.
راه اندازی Blazor Side
اکنون که پروژه React را پیکربندی کرده اید، زمان آن رسیده است که آن را با Blazor ادغام کنید تا بتواند برنامه React را میزبانی کند. این شامل استفاده از Initialize عملکردی که قبلاً در React اعلام شد entrypoint.jsx فایل
مراحل:
-
طرح 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"> -
یک کلاس 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>(); - این کلاس به صورت پویا ماژول جاوا اسکریپت برنامه React را وارد می کند (
-
رندر برنامه 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 تا اجزای خود را رندر کند.
- مرجع را به شیوه نامه React ایجاد شده اضافه کنید
-
اجرا و تأیید کنید
برنامه Blazor را با استفاده از
dotnet runدستور دهید و به مسیر پیکربندی شده بروید (مثلاً/) در مرورگر شما. باید برنامه React را در DOM برنامه Blazor مشاهده کنید.اگر تغییراتی در برنامه React ایجاد شد، باید آن را با استفاده از:
pnpm run buildاین باعث بازسازی مجدد می شود
ReactApp.jsوReactApp.cssفایل های داخل Blazorwwwroot/reactدایرکتوری
قابلیت همکاری دو طرفه را اضافه کنید
تا کنون، ما React را به گونهای پیکربندی کردهایم که در داخل کامپوننت Blazor با استفاده از Initialize تابع در حالی که این راهاندازی ارتباط مستقیم را امکانپذیر میکند – از NET (Blazor) به React – ما میتوانیم آن را گسترش دهیم تا به React اجازه دهیم به داتنت تماس بگیرد و تعامل دوطرفه کامل را ممکن میسازد. این با استفاده از جاوا اسکریپت Interop Blazor ([JSInvokable]) در کنار پیشرفت هایی در نقطه ورود React.
این قابلیت همکاری میتواند برای سناریوهایی مانند بهروزرسانی مؤلفههای React UI بر اساس دادههای واکشی شده از یک سرویس Blazor یا راهاندازی منطق Blazor از اقدامات کاربر در برنامه React مفید باشد.
مراحل:
-
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، برای جلوگیری از نشت حافظه. -
به روز رسانی 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 نیاز دارند.



