Blazor 應用程式有提供從 JavaSript 呼叫 .NET Method 的方法,當然反過來也可以從 .NET Method 叫用 JavaScript Function,打通兩個語言之間的邊界,讓互動性更上一層樓!
在 Blazor 中想要撰寫JS,有五種方式可以挑選
- 在
<head>
標記 中載入指令碼 (不推薦)
1
2
3
4
5
6
7
8
| <head>
<script>
window.jsMethod = () => {
alert("Hello World!")
};
</script>
</head>
|
不推薦寫在 head
標記內的原因有兩個
- 如果指令碼相依於 Blazor,則 JS Interop 可能會失敗
- 頁面可能會因為解析 JS 所需的時間增加而變慢
- 在
<body>
標記中載入指令碼
1
2
3
4
5
6
7
8
9
| <body>
...
<script src="_framework/blazor.{webassembly|server}.js"></script>
<script>
window.jsMethod = () => {
alert("Hello World!")
};
</script>
</body>
|
- 從外部 JavaScript 檔案載入指令碼
這是最熟悉的方式,獨立的Javascript檔案從外部引入到檔案內,可以將Javascript 放置於wwwroot內
1
2
3
4
5
6
| <body>
...
<script src="_framework/blazor.{webassembly|server}.js"></script>
<script src="{要引入的Javascript檔名.js}"></script>
</body>
|
- 從外部 JavaScript 檔案載入指令碼 (.js) 與元件共置
將 JavaScript 檔案組合在一起,以用於頁面(Page)、檢視(View)和 Razor 元件,是組織指令碼的便利方式,在 Blazor 中可以在元件的後方加上
.js
結尾,看起來像這樣.razor.js
Pages/Index.razor.js
1
2
3
| export function showHello() {
return prompt('Hello');
}
|
Pages/Index.razor
1
2
| module = await JS.InvokeAsync<IJSObjectReference>(
"import", "./Pages/Index.razor.js");
|
- 在 Blazor 啟動後插入指令碼
- 在引入 Blazor 的 JS 時加入屬性
autostart="false"
- 在
Blazor.start().then
Blazor 啟動後,在建立Sctipt區塊填入JS檔案
1
2
3
4
5
6
7
8
9
10
11
12
13
| <body>
...
<script src="_framework/blazor.{webassembly|server}.js"
autostart="false"></script>
<script>
Blazor.start().then(function () {
var customScript = document.createElement('script');
customScript.setAttribute('src', 'scripts.js');
document.head.appendChild(customScript);
});
</script>
</body>
|
不要把 <script>
放在 .razor 內,因為 Blazor 無法動態更新
.NET 叫用 JavaScript 函式#
想要在 .Net 內呼叫 JS 方法,首先需要 IJSRuntime
這一個介面,他的實作由 Blazor 框架註冊,這部分介紹兩個方法
- JSRuntimeExtensions.InvokeAsync
這個方法適用於呼叫 JavaScript 函式並讀取傳回的值,用起來的效果像是這樣
JavaScriptRun.razor.js
1
2
3
| export function getHello() {
return 'Hello';
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| @page "/JsRun"
@inject IJSRuntime JS
<button @onclick="getHello">Click</button>
<p>@text</p>
@code {
private IJSObjectReference? module;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
module = await JS.InvokeAsync<IJSObjectReference>("import", "./Pages/JavaScriptRun.razor.js");
}
}
private string text{ get; set; }
private async Task getHello()
{
text = await module.InvokeAsync<string>("getHello");
}
}
|
這邊採用的是上面的第四種方式: 從外部 JavaScript 檔案載入指令碼 (.js) 與元件共置
- JSRuntimeExtensions.InvokeVoidAsync
這個則是用於呼叫沒有回傳值的 JS 方法
1
2
3
4
5
6
7
8
9
10
11
| @page "/JsRun"
@inject IJSRuntime JS
<button @onclick="AlertHello">Click</button>
@code {
private async Task AlertHello()
{
await JS.InvokeVoidAsync("alert", "Hello");
}
}
|
JS Interop 呼叫預設都是非同步的,如果應用程式只有在 Blazor WebAssembly 執行,可以選擇進行同步 JS Interop 呼叫,這樣會稍微少一些額外負荷,而且可能會縮短轉譯週期
JavaScript 叫用 .NET#
接下來我們說說,從 JS 呼叫 .NET Method的方法
- DotNet.invokeMethodAsync
這是一個非同步的方法,推薦使用這個來做 .NET 方法的呼叫
語法看起來長這樣
1
| DotNet.invokeMethodAsync('{組件名稱}', '{方法名稱}', {參數});
|
- DotNet.invokeMethod
同步方法,僅對 Blazor WebAssembly 應用程式同步
想要讓 JS可以呼叫到 .NET 有幾個先決條件
- 方法必須是公開的(Public)
- 需要加入
[JSInvokable]
Attribute
JsRun.razor
1
2
3
4
5
6
7
8
9
10
11
12
13
| @page "/JsRun"
<button onclick="getString()">
Click
</button>
@code {
[JSInvokable]
public static async Task<string> GetHello()
{
await Task.CompletedTask;
return "Hello";
}
}
|
仔細看在Button Click的事件,沒有加入 @
代表這不是 Razor 語法
JsRun.js
1
2
3
4
5
6
| window.getString = () => {
DotNet.invokeMethodAsync('BlazorApp1', 'GetHello')
.then(data => {
console.log(data);
});
};
|
填入組件名稱以及方法名稱,就可以從JS呼叫 .NET 靜態方法了
JsRun.razor
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
| @page "/JsRun"
@implements IDisposable
@inject IJSRuntime JS
<button @onclick="JsCallInstanceMethod">
按我呼叫C# 執行個體方法
</button>
<P>@result</P>
@code {
private string? result;
private DotNetObjectReference<JsRun>? objRef;
public async Task JsCallInstanceMethod()
{
objRef = DotNetObjectReference.Create(this);
result = await JS.InvokeAsync<string>("sayHello", objRef);
}
[JSInvokable]
public string GetHelloMessage()
{
return $"Hello !";
}
public void Dispose()
{
objRef?.Dispose();
}
}
|
JsRun.razor.js
1
2
3
| window.sayHello = (dotNetHelper) => {
return dotNetHelper.invokeMethodAsync('GetHelloMessage');
};
|
呼叫執行個體方法稍稍複雜了一些,我們一步一步看他是怎麼執行的
- 首先按鈕按下去後,執行
JsCallInstanceMethod()
,建立新的 DotNetObjectReference<JsRun>
執行個體 - 執行 JS 的
sayHello
Function,調用 .NET GetHelloMessage
方法,完成後結果回傳給result
- 最後為了避免記憶體洩漏以及允許GC,
DotNetObjectReference
建立的執行個體要被放在 Dispose
內
就這樣我們一來一回的完成兩種語言的相互呼叫,在JS互通其實還有很多好玩的東西和細節,像是可以直接把stream
從 JS 傳輸到 .NET 、Blazor 在 JavaScript Module 有啟用JavaScript 隔離再也不用擔心全域汙染的問題……等等,一時半刻也說不清,在後續的章節慢慢補充說明
寫完這篇才發現忘記介紹在 Blazor 該怎麼寫 CSS 了XDD
下一篇文章回頭說說 Blazor 元件 CSS