2019年4月中旬微軟推出了 Blazor,當時的我感覺 Blazor 簡直是個劃時代的東西,竟然能讓 C# 執行到瀏覽器中。想著以後可能沒有 C/S 架構什麼事兒了,可以解除安裝掉所有的 App,電腦上有個瀏覽器就夠了。近來終於有了能夠系統瞭解 Blazor 的時間,下面把我瞭解到資訊寫下來分享給你,希望能對你有所幫助。
Blazor 可以使用強型別語言 C# 來替換 JavaScript 實現邏輯,可以前後端複用邏輯,也可以使用 nuget 1 中的包。它的其中一部分是基於 WebAssembly 實現的,所以不依賴任何的外掛,讓我們來快速瞭解一下 Blazor 是如何讓 C# 執行在瀏覽器中的吧。
Blazor 涉及技術
Blazor 2 是 apt.net core 生態的組成部分,所涉及到的技術也大部分和 .net 相關。
檢視層,使用 Razor 3 技術進行前端的編排渲染。Razor是一種標記語法,是 asp.net core 的預設檢視語法,最顯著的特點是強型別(C#、VB等)的程式碼可以和 HTML 寫在一個檔案中,當然也可以分開。在 razor 檔案中,@符號後面的都是強型別語言,可以是一行中的一部分,也可以是一整行,還可以是一個段落。在 asp.net core 中的大致做法是把 VB、C# 等強型別語言嵌入到網頁,當網頁被請求的時候,在伺服器端執行嵌入的程式碼,動態生成頁面。
以 Blazor WebAssembly 開發方式執行時,依賴 WebAssembly 4 技術,可以做成靜態頁面不依賴任何後端伺服器。
以 Blazor Server 方式開發執行時,依賴 SignalR 5 技術,並且需要後端伺服器端配合。
Blazor 簡介
Blazor 是一個使用 .NET 生成的互動式客戶端 Web UI 的框架。和前端同學所熟知的 Vue、React、Angular 有巨大差異。
其最大的特色是使用 C# 程式碼(理論上可以是 .NET 生態的任何語言)代替 JavaScript 來實現邏輯。
有兩種不同開發模式
Blazor WebAssembly, C# 程式碼執行在瀏覽器中。
Blazor Server,C# 程式碼在伺服器端執行,使用 SignalR 同步到瀏覽器進行更新。
Blazor WebAssembly
讓我們先簡單瞭解一下 WebAssembly (Wasm)4。
WebAssembly 是一種基於堆疊虛擬機器的二進位制指令格式。
WebAssembly 是一種類組合語言。
WebAssembly 效能高、可以和JavaScript協作。
WebAssembly 是W3C的一部分,現代化的瀏覽器都已經支援。
那麼現在讓我們來思考一個問題,如何讓 C# 程式碼,在瀏覽器中執行?
首先簡單回憶一下 C# 程式碼是如何執行起來的。如下圖所示,我們首先要把 C# 原始碼進行編譯成 dll 檔案,然後由 .net runtime 進行載入執行。
那如何讓 C# 在瀏覽器中執行起來呢?看起來只缺少一個在瀏覽器中執行的 .net runtime。所以要用 JavaScript 來寫一個 .net runtime 嗎?不要著急,dotnet 社群有更好的方案。
在 dotnet/runtime 6 原始碼的 Directory.Build.props 7 檔案中,我們可以看到,是有以 wasm 為編譯目標的,也就是基於 WebAssembly 實現的 .net runtime 。現在我們已經知道 C# 程式碼可以執行在瀏覽器中了, Blazor 的生存土壤也就有了。(當然,事實情況是 mono 社群最先實現 WebAssembly 版本的 .net runtime。後來才合併到 dotnet core runtime 中)
接下來看一下如何建立 Blazor 工程。有兩種方式,1、使用命令列,2、使用IDE,如 Visual Studio 。
命令列工具,僅供不想安裝 Visual Studio 這類巨型工具,還想嚐鮮的同學使用。可搭配任何編輯器,比如 Visual Studio Code。以下是具體步驟:
首先,需要下載 dotnet core 8。這裡順便給出 dotnet core 刪除工具 dotnet-core-uninstall 9 。
使用命令 donet new -l
可以看到所有支援的模板。
在空白目錄中使用命令 dotnet new blazorwasm
來建立 Blazor WebAssembly 的模板工程。
執行命令 dotnet run
啟動網站,可以在瀏覽器中訪問了。
使用 Visual Studio 建立就更為簡單,選擇對應的模板即可。
終於到了能上程式碼的時間了!
先來看兩個關鍵檔案,C# 的入口檔案 Program.cs 和 HTML 的入口檔案 index.html 。
// Program.cs 檔案
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Net.Http;
using System.Threading.Tasks;
namespace BlazorWebAssembly
{
public class Program
{
public static async Task Main(string[] args)
{
var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.Add<App>("#app");
// 上一行我們可以理解為,在 ID 是 app 的元素中渲染展示 Blazor WebAssembly,所以讓網頁的某一部分使用 Blazor 技術渲染也是可行的。
builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
await builder.Build().RunAsync();
}
}
}
<!-- index.html 檔案 -->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<title>BlazorWebAssembly</title>
<base href="/" />
<link href="css/bootstrap/bootstrap.min.css" rel="stylesheet" />
<link href="css/app.css" rel="stylesheet" />
<link href="BlazorWebAssembly.styles.css" rel="stylesheet" />
</head>
<body>
<div id="app">Loading...</div>
<div id="blazor-error-ui">
An unhandled error has occurred.
<a href="" class="reload">Reload</a>
<a class="dismiss">X</a>
</div>
<script src="_framework/blazor.webassembly.js"></script>
<!-- blazor.webassembly.js 檔案的作用有兩個。
1、下載 .net runtime, 依賴的類庫以及專案編譯後的檔案。
2、從 blazor.boot.json 中找到 C# 程式入口,並初始化執行。 -->
</body>
</html>
在編譯之後的檔案中,我們可以看到一些字尾名是 br 的檔案,是因為 Blazor 預設使用了壓縮率更高的 brotli 10 進行壓縮,我覺得在這裡應該推薦一下,brotli 比 gzip 更能節省空間。
Blazor WebAssembly 方式開發的簡單架構,如上圖所示,.net 程式碼的執行和 DOM 元素的渲染重繪都是在瀏覽器中進行的。
缺點:首次開啟網站時需要載入大量依賴檔案( .net runtime 以及依賴的各種類庫);需要瀏覽器支援 WebAssembly 。在 caniuse 中可以檢視到各瀏覽器對 WebAssembly 的支援情況 11 。
C# 和 JavaScript 的互動操作
有兩種不同的互動方式,1、從 C# 呼叫 JavaScript 的程式碼 12,2、從 JavaScript 呼叫 C# 的程式碼 13。
首先,從 C# 呼叫 JavaScript 的程式碼
先寫一個 JavaScript 的全域性函式,準備給 C# 使用。
function buildObjctString(name, age) {
const obj = { name, age }
return JSON.stringify(obj)
}
在 C# 中就可以這麼呼叫
@inject IJSRuntime JS
private async Task CSharpCallJS()
{
var methodName = "buildObjctString";
var name = "zpfe group";
var age = 18;
var data = await JS.InvokeAsync<string>(methodName, name, age);
}
然後,從 JavaScript 呼叫 C# 的程式碼
// 這裡的 C# 程式碼也是拿全域性函式來示例,在函式頭上加了註解。
[JSInvokable]
public static Task<string> GetTime(string param)
{
return Task.FromResult($"{param},後面的來自C# {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}");
}
在 JavaScript 中的呼叫方式是
// 如果 C# 函式有引數,不傳遞會報錯
const result = await DotNet.invokeMethodAsync("BlazorWebAssembly", "GetTime", "這個是 js 傳入的引數");
alert(result)
這裡只是最簡單的例子,更詳盡的還請從參考連結 1213中檢視 。
除錯
使用 Visual Studio 除錯將會很容易,和普通的 C# 除錯方式一樣,只在需要除錯的程式碼最前面用滑鼠點出除錯的斷點,按下 F5 就可以除錯了。JavaScript 程式碼也可以使用相同的方式進行除錯。
Blazor Server
個人認為 Blazor Server 是為了彌補 Blazor WebAssembly 的不足而創造出來的。Blazor Server 開發方式將會強依賴 SignalR 5 。這裡先簡單介紹一下 SignalR ,它也源自 ASPNET Core 社群,是基於 RPC 協議的一種實現。簡單來說,就是可以在瀏覽器中使用 JavaScript 程式碼通過 SignalR 來呼叫伺服器上開發的函式,也可以在伺服器上使用 SignalR 來呼叫瀏覽器中的 JavaScript 函式。這種方式能夠避免網站首次開啟時需要載入大量依賴檔案的問題,也能夠彌補瀏覽器對WebAssembly的支援問題,甚至能夠在 IE 中執行。但是這種方式開發的網站 UI 更新、事件處理等都需要通過 SignalR 呼叫伺服器進行處理,需要和伺服器頻繁互動資料,對伺服器算力要求頗高。以我的理解這種方式只適合使用者量不大的網站。
使用命令建立的方式是 dotnet new blazorserver
。
使用 Visual Studio 建立就不講了。
以 Blazor Server 開發方式的簡單架構,如上圖所示。 所有使用 C# 處理的 UI 更新,都需要在伺服器端進行,通過 SignalR 技術將更新結果傳送到瀏覽器中進行更新。用 C# 編寫的事件處理函式以及 C# 和 JavaScript 之間的互操作 ,也需要相同的邏輯。
Other
我覺得比較有意思的兩個功能 1、Blazor Destkop (.net core 6 將會支援),使用 Blazor WebAssembly 的方式開發桌面應用。2、Browser extension (開源社群的創意 14),使用 Blazor WebAssembly 的方式開發瀏覽器外掛。
更多更有意思的專案請關注 awesome blazor 15。
文中涉及到的程式碼已經共享到了 GitHub 16。
- https://www.nuget.org/ ↩
- https://docs.microsoft.com/zh... ↩
- https://docs.microsoft.com/zh... ↩
- https://webassembly.org/ ↩
- https://docs.microsoft.com/zh... ↩
- https://github.com/dotnet/run... ↩
- https://github.com/dotnet/run... ↩
- https://dot.net ↩
- https://github.com/dotnet/cli... ↩
- https://github.com/google/brotli ↩
- https://caniuse.com/?search=W... ↩
- https://docs.microsoft.com/zh... ↩
- https://docs.microsoft.com/zh... ↩
- https://github.com/mingyaulee... ↩
- https://github.com/AdrienTorr... ↩
- https://github.com/cnryb/blaz... ↩