مقدمة

إذا كنت مطور ويب وتعمل على تطوير مستعرض ، فأنت بالتأكيد على دراية JS ، والتي يمكن تشغيلها داخل المستعرض. هناك رأي مفاده أن JS ليست مناسبة للغاية للحسابات والخوارزميات المعقدة. وعلى الرغم من أن JS حققت قفزة كبيرة في السنوات الأخيرة في الأداء واتساع نطاق الاستخدام ، إلا أن العديد من المبرمجين ما زالوا يحلمون بإطلاق لغة النظام داخل المتصفح. في المستقبل القريب ، قد تتغير اللعبة بسبب WebAssembly.
لا تقف شركة Microsoft ثابتة وتحاول بنشاط نقل منفذ .NET إلى WebAssembly. كأحد النتائج ، حصلنا على إطار جديد لتطوير العميل - Blazor. لم يتضح بعد ما إذا كان Blazor يمكن أن يكون أسرع من الأطر JS الحديثة مثل React و Angular و Vue بسبب WebAssembly. لكن له بالتأكيد ميزة كبيرة - يمكن استخدام التطوير في C # ، بالإضافة إلى العالم كله من .NET Core داخل التطبيق.
تجميع وتنفيذ C # في Blazor
تعتبر عملية تجميع وتنفيذ لغة معقدة مثل C # مهمة معقدة وتستغرق وقتًا طويلاً. #?
- يعتمد على قدرات التكنولوجيا (أو بالأحرى ، جوهر). ومع ذلك ، فإن Microsoft ، كما اتضح فيما بعد ، قد أعدت بالفعل كل شيء لنا.
أولاً ، قم بإنشاء تطبيق Blazor.

بعد ذلك ، تحتاج إلى تثبيت Nuget - حزمة لتحليل وتجميع C #.
Install-Package Microsoft.CodeAnalysis.CSharp
تحضير صفحة البداية.
@page "/" @inject CompileService service <h1>Compile and Run C# in Browser</h1> <div> <div class="form-group"> <label for="exampleFormControlTextarea1">C# Code</label> <textarea class="form-control" id="exampleFormControlTextarea1" rows="10" bind="@CsCode"></textarea> </div> <button type="button" class="btn btn-primary" onclick="@Run">Run</button> <div class="card"> <div class="card-body"> <pre>@ResultText</pre> </div> </div> <div class="card"> <div class="card-body"> <pre>@CompileText</pre> </div> </div> </div> @functions { string CsCode { get; set; } string ResultText { get; set; } string CompileText { get; set; } public async Task Run() { ResultText = await service.CompileAndRun(CsCode); CompileText = string.Join("\r\n", service.CompileLog); this.StateHasChanged(); } }
تحتاج أولاً إلى تحليل السلسلة إلى شجرة بناء جملة مجردة. نظرًا لأننا في الخطوة التالية سنقوم بتجميع مكونات Blazor - نحتاج إلى أحدث إصدار ( LanguageVersion.Latest
) من اللغة. هناك طريقة لـ Roslyn لـ C # لهذا:
SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText(code, new CSharpParseOptions(LanguageVersion.Latest));
بالفعل في هذه المرحلة ، يمكنك اكتشاف أخطاء الترجمة الإجمالية عن طريق قراءة تشخيصات المحلل اللغوي.
foreach (var diagnostic in syntaxTree.GetDiagnostics()) { CompileLog.Add(diagnostic.ToString()); }
بعد ذلك ، نقوم بتجميع Assembly
في دفق ثنائي.
CSharpCompilation compilation = CSharpCompilation.Create("CompileBlazorInBlazor.Demo", new[] {syntaxTree}, references, new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)); using (MemoryStream stream = new MemoryStream()) { EmitResult result = compilation.Emit(stream); }
تجدر الإشارة إلى أنك تحتاج إلى الحصول على references
- قائمة بيانات التعريف الخاصة بالمكتبات المتصلة. لكنني لم أستطع قراءة هذه الملفات على طول مسار Assembly.Location
، حيث لا يوجد نظام ملفات في المتصفح. قد تكون هناك طريقة أكثر فاعلية لحل هذه المشكلة ، ولكن الغرض من هذه المقالة هو فرصة مفاهيمية ، لذلك سنقوم بتنزيل هذه المكتبات مرة أخرى عبر Http ونقوم بذلك فقط في بداية التجميع الأولى.
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) { references.Add( MetadataReference.CreateFromStream( await this._http.GetStreamAsync("/_framework/_bin/" + assembly.Location))); }
من EmitResult
يمكنك معرفة ما إذا كانت المجموعة ناجحة ، وكذلك الحصول على أخطاء تشخيصية.
الآن تحتاج إلى تحميل Assembly
إلى AppDomain
الحالي وتنفيذ التعليمات البرمجية المترجمة. لسوء الحظ ، لا توجد طريقة لإنشاء AppDomain
متعددة داخل المستعرض ، لذلك لن يعمل على تحميل وتفريغ Assembly
بأمان.
Assembly assemby = AppDomain.CurrentDomain.Load(stream.ToArray()); var type = assemby.GetExportedTypes().FirstOrDefault(); var methodInfo = type.GetMethod("Run"); var instance = Activator.CreateInstance(type); return (string) methodInfo.Invoke(instance, new object[] {"my UserName", 12});

في هذه المرحلة ، قمنا بتجميع وتنفيذ شفرة C # مباشرة في المتصفح. يمكن أن يتكون البرنامج من عدة ملفات واستخدام مكتبات .NET أخرى. أليس هذا رائعا؟ الآن دعنا ننتقل.
تجميع وتشغيل مكون Blazor في المستعرض.
مكونات Blazor هي قوالب Razor
المعدلة. لذلك ، لتجميع مكون Blazor ، تحتاج إلى نشر بيئة كاملة لتجميع قوالب Razor وتكوين ملحقات لـ Blazor. تحتاج إلى تثبيت حزمة Microsoft.AspNetCore.Blazor.Build
من nuget. ومع ذلك ، ستفشل إضافته إلى مشروع Blazor الخاص بنا ، لأنه بعد ذلك لن يتمكن الرابط من تجميع المشروع. لذلك ، تحتاج إلى تنزيله ، ثم إضافة 3 مكتبات يدويًا.
microsoft.aspnetcore.blazor.build\0.7.0\tools\Microsoft.AspNetCore.Blazor.Razor.Extensions.dll microsoft.aspnetcore.blazor.build\0.7.0\tools\Microsoft.AspNetCore.Razor.Language.dll microsoft.aspnetcore.blazor.build\0.7.0\tools\Microsoft.CodeAnalysis.Razor.dll
لنقم بإنشاء نواة لتجميع Razor
وتعديله لـ Blazor ، حيث إن النواة ستقوم بشكل افتراضي بإنشاء شفرة Razor للصفحات.
var engine = RazorProjectEngine.Create(BlazorExtensionInitializer.DefaultConfiguration, fileSystem, b => { BlazorExtensionInitializer.Register(b); });
للتنفيذ ، نظام fileSystem
هو الوحيد fileSystem
- وهذا هو تجريد على نظام الملفات. ومع ذلك ، قمنا بتطبيق نظام ملفات فارغ ، إذا كنت ترغب في ترجمة مشاريع معقدة مع دعم _ViewImports.cshtml
، فأنت بحاجة إلى تنفيذ بنية أكثر تعقيدًا في الذاكرة.
الآن سنقوم بإنشاء الشفرة من مكون Blazor في كود C #.
var file = new MemoryRazorProjectItem(code); var doc = engine.Process(file).GetCSharpDocument(); var csCode = doc.GeneratedCode;
يمكنك أيضًا تلقي رسائل تشخيصية من doc
حول نتائج إنشاء رمز C # من مكون Blazor.
الآن وصلنا رمز المكون C #. تحتاج إلى SyntaxTree
، ثم ترجمة التجميع ، SyntaxTree
في AppDomain الحالي والعثور على نوع المكون. كما في المثال السابق.
يبقى لتحميل هذا المكون في التطبيق الحالي. هناك عدة طرق للقيام بذلك ، على سبيل المثال ، عن طريق إنشاء RenderFragment
الخاص بك.
@inject CompileService service <div class="card"> <div class="card-body"> @Result </div> </div> @functions { RenderFragment Result = null; string Code { get; set; } public async Task Run() { var type = await service.CompileBlazor(Code); if (type != null) { Result = builder => { builder.OpenComponent(0, type); builder.CloseComponent(); }; } else { Result = null; } } }

الخاتمة
قمنا بتجميع وإطلاق المكون في متصفح Blazor. من الواضح أن تجميعًا كاملاً لرمز C # الديناميكي داخل المستعرض يمكن أن يثير إعجاب أي مبرمج.
ولكن هنا يجب أن تفكر في مثل هذه "العثرات":
- هناك حاجة إلى ملحقات
bind
إضافية ومكتبات لدعم bind
ثنائي الاتجاه. - لدعم
async, await
، وبالمثل نقوم بتوصيل إضافية. المكتبات - يتطلب تجميع المكونات المتعلقة بـ Blazor تجميعًا من خطوتين.
تم حل جميع هذه المشكلات بالفعل وهذا موضوع لمقال منفصل.
بوابة
عرض