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 نیاز دارند.