Quantcast
Channel: ささいなことですが。
Viewing all articles
Browse latest Browse all 104

C#だけでJavaScriptを書く! Blazor.DynamicJSを作りました ①

$
0
0

2023/03/03 にある meetup app osaka@7 で話すやつです。
meetupapp.connpass.com

BlazorでちょいちょいJavaScript書くときありますよね、でもちょっとしたやつやからJavaScript別に書くの面倒なんですよねー、でDynamic使ったらラップできるんじゃないかってことでやってみました。Nugetにも公開しててコードはこちら
github.com

こんな感じで書けます。

//JavaScriptを書くためのオブジェクト作成usingvar js =await JS.CreateDymaicRuntimeAsync();

//ここからはdynamicでJavaScript書けます。dynamic window = js.GetWindow();

dynamic button = window.document.createElement("button");
button.innerText ="new button";

dynamic div = window.document.getElementById("parent-div");
div.append(button);

//コールバックも書けます
button.addEventListener("click", (Action<dynamic>)(e => {
    string detail = e.detail.toString();
    window.console.log($"clicked {detail}");
}));

結構頑張っててコールバックもかけるんですよね。「遅いんじゃないの?」って言われそうですけど、まあそのとおりですねw、一行につき一回くらJavaScriptの呼び出しが入ります。とはいえそんな速くはないけどWebAssemblyだとこれくらいの行数やったら実使用上は問題ないんちゃうかな?

グローバルなやつだけじゃなくてimportもこんな感じで書けます。

usingvar js =await JS.CreateDymaicRuntimeAsync();

//絶対パスを指定してねdynamic mod =await _js!.ImportAsync("/module.js");

//module.js に sum ってメソッドがあるとしてint sum = mod.sum(1, 2, 3, 4);

いい感じの書き心地ですよね。ここまではよかったんですけどね・・・、new と 非同期がいまいち。まずは new

<script>    Rectangle: class{        constructor(height, width){this.height = height;this.width = width;}}</script>
usingvar js =await JS.CreateDymaicRuntimeAsync();
dynamic window = js.GetWindow();

//new XXXってやる部分を JSSynctaxで囲むdynamic rect =new JSSyntax(window.Rectangle).New(10, 20);

//moduleの場合も同様dynamic mod =await _js!.ImportAsync("/module.js");
//moduleにもRectangleが定義されているとしてdynamic rect2=new JSSyntax(mod.Rectangle).New(10, 20);

//あんまりなんでグローバルのクラスはこう書けるようにもしてますvar rect3 = js.New("Rectangle", 1, 2);

JSSyntaxってのが苦肉の策。ここはシンタックスなんですよー、シンタックスに対する操作なんですよーって感じ。非同期も同様に JSSyntax で囲むようにしました。

usingvar js =await JS.CreateDymaicRuntimeAsync();
dynamic window = js.GetWindow();

dynamic div = window.document.getElementById("parent-div");

awaitnew JSSyntax(div.append).InvokeAsync(button);

//コールバックも非同期にできます
button.addEventListener("click", (Func<dynamic, Task>)(async e =>
{
    await Task.CompletedTask;
    string detail = e.detail.toString();
    window.console.log($"clicked {detail}");
}));

でも正直 WebAssemblyの方は別にasyncでなくてもええんちゃう?って思って僕は非同期使ってないけどどうなんやろ?あとmodule使うにせよmoduleのクラスをC#からnewすることってないから(グローバルはjs.New()でいけるようにしたし)僕が使う分には許容範囲の書き心地です。

Blazor.DynamicJS管理下のJavaScriptオブジェクト

dynamicで受けたやつは何かというと以下二つです。
①呼び出し情報
JavaScriptオブジェクト(numberとかも含む)

②はそれに対して操作することもできるし、それがJsonにできるものであればC#の世界に持ってこれます。

dynamic window = js.GetWindow();

//これはまだJavaScriptの呼び出しは発生していないくて、"window.document"っていう文字列の呼び出し情報dynamic document = window.document;

//ここで JavaScript を呼び出して戻り値のボタンは Blazor.DynamicJSのヘルパJavaScriptで管理していてそのIDを返してます。dynamic button = window.document.createElement("button");

//buttonを操作するときはそのIDとプロパティ名とかで呼び出す感じ
button.innerText ="new button";

//dynamic以外の型に変換するとシリアライズされて持ってこれるstring innerText = button.innerText;

引数

引数はもともとの IJSRuntime に渡せるもの(Jsonにできるものと ElementReference)と前述したJavaScriptオブジェクトを渡せます。

dynamic button = window.document.createElement("button");
dynamic div = window.document.getElementById("parent-div");
//引数にJavaScriptのオブジェクトを渡してます。
div.append(button);

実はFriendlyと同じ設計思想

Friendlyは別プロセスのメソッドをリフレクションで呼び出して、これはJavaScriptに対して同じ思想でやってるだけでした。オブジェクトの管理とかシリアライズとか引数のルールとかも実は同じです。

連載物です。

「ちょっとやってみるかなー」って軽く始めたけど結構ガッツリ実装ました。これは dynamic を使って緩く書く書き方なんですけど、DispatchProxy で型を決めて呼び出す方式も実装しちゃったんですよね。なんであと何回か書きます。内部のつくりとかも書けたら書きます。


Viewing all articles
Browse latest Browse all 104

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>