序文
バックエンド プログラマーにとって、フロントエンドは非常に単純に見えますが、3 つの一般的なフロントエンド フレームワークなど、多くのフレームワークがありますVue.js、Angular.js、React.js
。各フレームワークは、さまざまなマッチング ライブラリに対応しており、フロントエンド開発用の完全な知識システムを構築することは容易ではありません。そして、画像技術や分散コンピューティング、クラウドレンダリングなどの一部のハイエンド技術は、フロントエンド技術と多少関係があるようで、フルスタック開発を行うという考えが生まれました。
フルスタックであるためには、まったく無関係な技術体系よりも、世界を席巻する技術が最も優れている.私はC++の研究開発に10年以上の経験があります. C++ のさまざまなシナリオの研究と開発. 他のテクノロジからのいくつかのバイアス. 最近、さまざまなプロジェクトに取り組んだ後、いくつかの言語の開発は本当に香りがよいことに気づきました (C# の拡張メソッド、リフレクション機構、Linq、パッケージ管理などにより、開発効率が大幅に向上しました; NODEJS フロントエンドとバックエンド-最後は一緒に開発され、一連のコードは1回のコンパイルで解決できます.マルチターミナルアプリケーション; VueフレームワークはDom操作を気にしません). テクノロジーは問題を解決するものであり、それ自体に良い悪いはありません。特定のシナリオで特定のテクノロジーをうまく使いこなし、プロジェクト内の問題を迅速かつ迅速に解決できる優れたテクノロジーです。フルスタック開発を行うためには、さまざまな言語のリンクを開拓することが最も重要です(C++をベースに、さまざまな言語の壁を打ち破るために)。
WebAssembly 技術は、バックエンド担当者がフロントエンド プログラムを開発できるようにする技術です.Blazor を使用する前は、C++ の EMSDK 技術を使用して C++ Webassembly プログラムを開発していました (プロジェクトの開発、コンパイル、デバッグ、インターフェイスの難しさは地獄レベルです。カプセル化は非常に時間がかかります。消費する)。そこで最近、他の言語での WebAssembly 開発に切り替えました。そのとき初めて、Microsoft の Blazor の C# WebAssembly のソリューションを発見しました。
ブレザーとは
Blazor は、Microsoft が .NET で立ち上げた WEB クライアント UI インタラクション フレームワークです. Blazor を使用すると、JavaScript を置き換えて独自のページ インタラクション ロジックを実装したり、C# コードを大幅に再利用したりできます. Blazor は .NET 開発者向けです.選択。
Blazor には 2 つのホスティング モードがあります。1 つは、asp.net コア デプロイに基づく Blazor サーバー モードで、クライアントとサーバー間のやり取りは SignalR を介して行われ、クライアント UI の更新と動作のやり取りを実現します。
もう 1 つは Blazor WebAssembly モードで、Blazor アプリケーション、その依存関係、および .NET ランタイムをブラウザーにダウンロードし (そのため、別のエンドで再コンパイルする必要がある場合があるという欠陥があります)、アプリケーションが実行されます。ブラウザ スレッドで直接。次の利点があります:
Blazor WebAssembly ホスティング モデルには、次の利点があります。
.NET サーバー側の依存関係はなく、アプリケーションはクライアントにダウンロードされた後、正常に実行できます。
クライアントのリソースと機能をフルに活用できます。
作業はサーバーからクライアントに転送できます。
ASP.NET Core Web サーバーを使用せずにアプリをホストします。アプリケーションがコンテンツ配信ネットワーク (CDN) 経由で提供されるシナリオなど、サーバーレス展開シナリオが可能です。
Blazor WebAssembly ホスティング モデルには、次の制限があります。
アプリはブラウザ機能のみを使用できます。
利用可能なクライアント ハードウェアとソフトウェア (WebAssembly サポートなど) が必要です。
ダウンロード サイズが大きく、アプリケーションの読み込みに時間がかかります。
.NET ランタイムとツールのサポートは未開発です。たとえば、.NET Standard のサポートとデバッグには制限があります。
この記事では主に WebAssembly の技術を紹介します。
ブレザーの取り付け
- Asp.net 環境をインストールし、WebAssembly コンポーネントがチェックされていることを確認します
- インストールが完了したら、VS を開いて Blazor プロジェクトを選択し、
Blazor WebAssembly
アプリケーションを選択して、既定のオプションを使用します。
- 図に示すようにプロジェクトを作成した後
- コンパイル後に実行
Blazor プロジェクトの構造
これは、Blazor WebAssembly を作成するための大まかなテンプレートです。
- カウンター コンポーネント (Counter.razor): 「カウンター」ページを実装します。
- FetchData コンポーネント (FetchData.razor): Fetch Data ページを実装します。
- インデックス コンポーネント (Index.razor): ホームページを実装します。
- Properties/launchSettings.json: 開発環境の構成を保持します。
- 共有フォルダー: 次の共有コンポーネントとスタイルシートが含まれています。
- MainLayout コンポーネント (MainLayout.razor): アプリケーションのレイアウト コンポーネント
- MainLayout.razor.css: メイン レイアウトを適用するスタイル シート。
- NavMenu コンポーネント (NavMenu.razor): サイドバー ナビゲーションを実装します。他の Razor コンポーネントへのナビゲーション リンクをレンダリングする NavLink コンポーネント (NavLink) が含まれています。NavLink コンポーネントは、そのコンポーネントが読み込まれると、選択された状態を自動的に示します。これにより、ユーザーは現在どのコンポーネントが表示されているかを理解できます。
- NavMenu.razor.css: ナビゲーション メニューのスタイル シートを適用します。
- SurveyPrompt コンポーネント (SurveyPrompt.razor): Blazor 調査コンポーネント (サブコンポーネント)
- wwwroot: アプリの Web ルート フォルダー。これには、appsettings.json や構成設定用のアンビエント アプリ設定ファイルなど、アプリの公開静的アセットが含まれます。index.html ページは、HTML ページとして実装されたアプリケーションのルート ページです。
アプリケーションの任意のページが最初に要求されると、それがレンダリングされ、応答で返されます。
このページでは、ルート アプリ コンポーネントがレンダリングされる場所を指定します。アプリの ID を使用する (読み込んでいます…) div DOM 要素の位置にコンポーネントをレンダリングします。
- _Imports.razor: 名前空間の @using ディレクティブなど、アプリケーション コンポーネント (.razor) に含める一般的な Razor ディレクティブが含まれています。これは、グローバル参照コンポーネント コマンドと同等です。
- App.razor: アプリケーションのルート コンポーネントであり、Router コンポーネントを使用してクライアント ルートを設定するために使用されます。Router コンポーネントは、ブラウザのナビゲーションをインターセプトし、要求されたアドレスに一致するページをレンダリングします。
- Program.cs: WebAssembly ホストを設定するためのアプリケーション エントリ ポイント:
App コンポーネントは、アプリケーションのルート コンポーネントです。ルート コンポーネント コレクション (builder.RootComponents.Add(“#app”)) の場合、アプリの ID を使用します (wwwroot/index.html 内)。
読み込んでいます…) は、App コンポーネントを div DOM 要素として指定します。
サービスが追加され、構成されます (例: builder.Services.AddSingleton<IMyDependency, MyDependency>())。
最初のページを書く
Blazor ページは Razor 構文を使用して記述されており、フロントエンド JS コードや HTML 記述とは異なり、Razor 構文は関連記事を参照できます
Blazor のページ構造は Vue の構造に似ており、ページ構造 + ロジック コードで構成されています。
@page "/counter" //页面路由,如果是一个独立的页面需要加上这个页面标记,如果作为一个子组件则没有必要
<PageTitle>Counter</PageTitle> //
<h1>Counter</h1>
<p role="status">Current count: @currentCount</p> //@是razor的语法,用于绑定C#的属性或者方法来的
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button> //直接调用方法
//C#代码块
@code {
private int currentCount = 0;
private void IncrementCount()
{
currentCount++;
}
}
一見すると戸惑うかもしれませんが、C#とページコードが一緒に書かれていますが、ページコードは実際にC#コードを呼び出すことができ、クラス構造はありません。このインターフェイスを理解するには、このコンポーネントをクラス オブジェクトとして扱う必要があります。ページとロジック コードは同じクラスに属します。2 つの異なるファイルにあるように見えることもありますが、これはクラス (ComponentBase クラスを継承) であり、さまざまな場所で。この C# の構成については、
カミソリ コンポーネントの C# コード構成フォームを参照してくださいhttps://www.cnblogs.com/harrychinese/p/blazaor_CSharp_code.html
、ここでカミソリの関連する命令をさりげなく紹介します
@code是Razor 组件专用,用于在@code代码块中定义页面类需要的字段,属性,方法等定义。@code是@functions的别名。但是建议在Razor组件中使用@code,而非@functions.
@implements 指令,类比如C# 类的implements.
@inherits: 类比C#的中继承,在Blazor的类的组织方式里面有说明
@inject: 从DI容器中引入需要的类,一个单例类的引入。在C#类中使用[inject]标记
@layout: 指定页面需要继承的布局模板。
@model: 专用于MVC 视图和Razor Page页面,用于指定model
@namespace: 用户指定生成的页面类所处的名称空间。
@page指令:用户razor page项目或者razor 组件,用于定义路由。
@preservewhitespace: 是否保留空白,默认是false.
@section: 用户razor page或者是mvc
Skia を使用して Blazor の描画機能を実装する
Blazor での描画を実現するには、html と同様の canvas 関数を使用し、ライブラリを使用してBlazor.Extensions.Canvas
実現することが. 内部には多くの例があります。プロジェクトではskiaライブラリがより多く使用されるため、skiaライブラリはBlazorの描画に使用されます。
1. Skia ライブラリのインストール
nuget を使用して SkiaSharp および SkiaSharp.Views.Blazor ライブラリをインストールします。SkiaSharp は基本ライブラリであり、SkiaSharp.Views.Blazor はサーフェスの表示を担当します。
2. 描画ページを作成する
- 描画ページを追加します。メイン ページ skiacanvas.razor を直接変更します
- skiacanvas.razor ページとのバインディングを実現するために、対応するページ コード クラス skiacanvas.razor.cs を追加します。
- skiacanvas.razor にラベルを追加し
SKCanvasView
、 IgnorePixelScaling プロパティを true に設定します (つまり、ピクセル スケーリングは無視されます。そうしないと、マウス イベントで取得されたオフセット値は、システムの画面スケーリング比に比例する実際の値とは異なります)。 @ref="canvasView" canvasView オブジェクトは、C# コードで定義されたメンバー変数ですSKCanvasView canvasView
<Layout ShowFooter="false"> // 这里面使用了BootstrapBlazor组件库进行页面布局
<Header>
<PageTitle>XDGraph WEB系统</PageTitle>
<br/>
</Header>
<Main>
<SKCanvasView @ref="canvasView" IgnorePixelScaling=true style="@CursorType" OnPaintSurface="OnPaintSurface" @onkeydown="OnCanvasKeyDown"
@onmousemove="OnCanvasMouseMove" @onwheel="OnCanvasMouseWheel" @onmousedown="OnCanvasMouseDown" tabindex="0"
@onmouseup="OnCanvasMouseUp"/>
</Main>
</Layout>
予防:
- デフォルトでは、SKCanvasView タグはキーボード イベント (html5 キャンバスと同様) をサポートしていません。キーボード イベントに応答するには、tabindex 属性を指定する必要があります。
- すべての描画は OnPaintSurface イベントで行われるため、OnPaintSurface イベントを実装する必要があります。
- canvasView バインディング オブジェクトの初期化は、対応する C# コードのコンストラクタではなく、ページの初期化が完了した後で行われます。
3. SkiaCanvas.Razor.cs コードの実装
以下はコードです。実際のコードをプロジェクトに直接投稿することはできません。
using SkiaSharp;
using SkiaSharp.Views.Blazor;
public partial class SkiaCanvas
{
protected SKCanvasView canvasView=null;
public void OnCanvasKeyDown(KeyboardEventArgs e)
{
//键盘事件
}
public void OnCanvasMouseWheel(WheelEventArgs e)
{
}
public void OnCanvasMouseMove(MouseEventArgs e)
{
var mouse_event = toEvent(e);
_component.MouseMove(mouse_event);
move_point =new Point(mouse_event.OffsetX,mouse_event.OffsetY);
}
public void OnPaintSurface(SKPaintSurfaceEventArgs e)
{
var canvas = e.Surface.Canvas;
//SKBitmap bitmap = new SKBitmap(e.Info.Width, e.Info.Height);
canvas.Clear(SKColors.White);
//调用底层函数绘制
_component.Graph.View.Redraw(canvas);
//绘制坐标;
using var paint = new SKPaint
{
Color = SKColors.Black,
IsAntialias = true,
Typeface = SkiaChinaFont.ChinaFont,
TextSize = 24
};
if (move_point != null)
{
//move_point是mousemove事件中获取的,直接在渲染接口中好像没有办法获取到;
var msg1 = $"x:{
move_point.X.ToString("F2")} y:{
move_point.Y.ToString("F2")}";
canvas.DrawText(msg1, 0, e.Info.Height-30, paint);
}
}
}
4.描画インターフェースの適応問題について
The window size of SKCanvasView is changed by the browser window. コンポーネントのラベル サイズ設定は、
style="width:900px;height=900px"
同様のコードで実現されます. デフォルトのフロントエンド言語を含む、razor コンポーネント コードには、対応するイベント応答はありません. 通常、それは処理されます。 window.reszie イベントをリッスンします。これには、C# と JS の間の対話が含まれます。幸いなことに、それをカプセル化したオープン ソース ライブラリがあり、BlazorPro.BlazorSize
このライブラリはサイズ変更への適応性の問題を解決します。詳しくはライブラリの使い方をご覧ください。
次のコードは、元のコードに次のコードを追加して実現します。
public static string DefaultStyle= "width:100%;heiht:100%;";
[Parameter]
public string CursorType {
get; set; } = DefaultStyle;
protected override void OnAfterRender(bool firstRender)
{
if (firstRender)
{
// Subscribe to the OnResized event. This will do work when the browser is resized.
listener.OnResized += WindowResized;
}
}
void WindowResized(object? sender,BrowserWindowSize window)
{
browser = window;
int width = browser.Width;
int height = browser.Height-100;
DefaultStyle = $"width:100%;height:{
height}px;";
CursorType = DefaultStyle;
StateHasChanged();
}
5.中国語のテキスト表示
デフォルトでは、Skia で描画された漢字は表示できません。このとき、中国語フォントの表示を追加する必要があります。
public static class SkiaChinaFont
{
public static SKTypeface ChinaFont {
get; private set; }
static SkiaChinaFont()
{
//加载资源方案,设置字体文件属性为嵌入的资源(需要注意)
try
{
//嵌入资源的访问是应用程序名加上路径,路径使用.而不是使用/。
//如XDGraphWeb.res.DroidSansFallback.ttf 表示的是应用程序名为XDGraphWeb 在当前项目的res文件夹下的DroidSansFallback.ttf文件.
var fontStream = Assembly.GetExecutingAssembly().GetManifestResourceStream("XDGraphWeb.res.DroidSansFallback.ttf");
ChinaFont = SKTypeface.FromStream(fontStream);
}
catch(Exception e)
{
var error=e.Message;
int c = 0;
}
}
}
6. 結果
Blazor の静的リソースへのアクセス
WebAssembly には 2 種類の静的リソースがあり、1 つは C# の組み込みリソースであり、もう 1 つは wwwroot ディレクトリ配下のファイル アクセスです。
1. 埋め込みリソース
私たちは通常、より多くのローカル ファイル システム、または FTP やオブジェクト ストレージなどのリモート サーバーで実行されているファイル システムと接触します. これらは非埋め込みリソースであるため、埋め込みリソースは主にディレクトリ階層リソースのないファイルを参照します。 " をコンパイル時にダイナミック リンク ライブラリ (DLL) に追加します。以前にskiaに導入されたリソースファイルは、組み込みリソースのアクセスに属します。
- Visual Studio で指定されたファイルを選択し、そのプロパティ ウィンドウで埋め込みリソースとしてビルド操作を選択します。
このようにして、埋め込みリソースの定義が完了しました。組み込みリソースの定義は、基本的に実行時に読み取って使用するためのものであるため、当然のことながら、これらの組み込みリソースをどのように読み取るかを尋ねずにはいられません。Assembly クラスでは、Microsoft は埋め込みリソースを処理するための次のインターフェイスを提供しています。
public virtual ManifestResourceInfo GetManifestResourceInfo(string resourceName);
public virtual string[] GetManifestResourceNames();//返回资源名称
public virtual Stream GetManifestResourceStream(Type type, string name);//获取资源中的留数据
public virtual Stream GetManifestResourceStream(string name);
この方法によりGetManifestResourceNames
、そのリソースが ABC パスを介してアクセスされることがわかりました。
var assembly = Assembly.GetExecutingAssembly();
var resources = assembly.GetManifestResourceNames();
resources.ToList().ForEach(x => Console.WriteLine(x));
var fileInfo = assembly.GetManifestResourceInfo(resources[0]);
var fileStream = assembly.GetManifestResourceStream(resources[0]);//获取文件的字节流
2.httpクライアント
HttpClient クラスが存在するライブラリは System.Net.Http であり、Blazor webassembly の既定のテンプレートでは、HttpClient が DI コンテナーに自動的に登録されています。これは非常に便利です。Program.Main 関数は、DI コンテナー コードを登録します。
builder.Services.AddScoped(sp => new HttpClient {
BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
HttpClient を直接使用する際の問題は次のとおりです。
- HttpClient の主な問題点は、Dispose 後もソケット接続をすぐに閉じることができないことです. Windows では、ソケットを閉じる前にデフォルトで 240 秒待機する必要があります. HttpClient を短時間に大量に使用すると、ソケットが使い果たされます.クライアントとサーバーの間の接続. See the analysis of reference document 1. したがって、クライアント アプリケーションは通常、シングルトン モードを使用して HttpClient クラスを使用します. 同じことが Blazor webassembly にも当てはまります.
- シングルトン モードを使用する場合、URL ごとに異なるヘッダーを設定するのは非常に不便です。
- HttpClient は IP もキャッシュします。DNS が後で更新された場合、HttpClient は引き続き古い IP を使用します。
したがって、通常、HttpClient はシングルトン クラスとして登録されます. シングルトンとして登録された後、Razor.cs 内のコードは次のようになります。
//直接获取单例类;
[Inject]
private HttpClient Http {
get; set; } //一定要设置为get;set 否则会崩溃。HttpClient只能设置在这样的组件类中设置,普通类设置无效还有可能出问题。
HttpClient の使用
// 读取html数据,在wwwroot目录下的数据
var xml_string= await Http.GetStringAsync("/sample-data/defaultConfig.Xml");
//获取json数据
protected override async Task OnInitializedAsync()
{
Items = await Http.GetFromJsonAsync<MenuItem[]>("sample-data/menu.json");
return;
}
Blazor 用の BootstrapBlazor インターフェイス ライブラリ
https://www.blazor.zone/introduction BootstrapBlazor インターフェース ライブラリは 200 近くのコンポーネントを提供し、基本的にフロントエンド開発のほとんどのインターフェース使用法をカバーします。
- ダイアログボックスの使用
@using System.ComponentModel.DataAnnotations
@using Microsoft.Extensions.Localization
@using System.Xml
<div>
<ValidateForm Model="Project" OnValidSubmit="OnSubmit">
<div class="row g-3">
<div class="col-12">
<InputUpload @bind-Value="@Project.Mxe" DisplayText="工程文件名" />
</div>
<div class="col-12">
<Button ButtonType="@ButtonType.Submit" Text="提交"></Button>
</div>
</div>
</ValidateForm>
</div>
@code
{
//表单验证设置
private class PersonInfo
{
[Required]
[FileValidation(Extensions = new string[] {
".mxe" }, FileSize = 2*1024 * 1024)]
public IBrowserFile? Mxe {
get; set; }
}
private PersonInfo Project {
get; set; } = new PersonInfo();
//对话框的参数传递,返回值必须是object
[CascadingParameter(Name = "BodyContext")]
public object? Graph {
get; set; }
//设置回调函数参数;
[Parameter]
public Action? OnClose {
get; set; }
//执行提交功能,打开本地文件,相当于实现了文件上传功能
private async Task OnSubmit(EditContext context)
{
var graph=Graph as XDGraph;
if(graph==null || Project.Mxe==null)
{
return;
}
MemoryStream ms = new MemoryStream();
await Project.Mxe.OpenReadStream().CopyToAsync(ms);
//从内存数据流中读取文件;
XmlDocument docxml = new XmlDocument();
byte[] b = ms.ToArray();
string s = System.Text.Encoding.UTF8.GetString(b, 0, b.Length);
docxml.LoadXml(s);
//删除历史;
graph.Editor().UndoManager().Clear();
var codec = new XDCodec();
graph.SetModel(codec.DecodeGraphModel(docxml, graph.Model));
graph.Repaint();
if(OnClose!=null)
{
OnClose.Invoke();
}
return ;
}
}
ダイアログ機能の使用
public async Task OpenFile()
{
var option = new DialogOption()
{
Title = "打开本地工程文件",
IsKeyboard=true,
ShowHeaderCloseButton=true
};
//也可以当成参数传递;
option.BodyContext = MainGrpah;
option.BodyTemplate = BootstrapDynamicComponent.CreateComponent<OpenDialogComponent>(new Dictionary<string, object?>
{
[nameof(OpenDialogComponent.OnClose)] = new Action(async () => await option.Dialog.Close())
}).Render();
await DialogService.Show(option);
}
Blazor関連の学習サイト
Github での Blazor の例https://github.com/syncfusion/blazor-samples
Blazor.Extensions.Canvas https://github.com/mizrael/BlazorCanvas
Web での SkiaSharp の描画https://www.cnblogs.com/ sunnytrudeau /p/15574467.html
ビビ ビデオ チュートリアルhttps://www.bilibili.com/video/BV19K4y1e7kd?p=1
Blazor 公式サイトhttps://docs.microsoft.com/zh-cn/aspnet/core/blazor/? view=aspnetcore-6.0
https://github.com/AdrienTorris/awesome-blaz
blazor インターフェイス ライブラリhttps://www.blazor.zone/introduction
blazor の C# 編成https://www.cnblogs.com/harrychinese/p / blazaor_CSharp_code.html
Blazor の C# と JS の相互運用性https://www.cnblogs.com/functionMC/p/16552500.html