五分鐘瞭解 Blazor

智聯大前端發表於2021-10-12

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 進行載入執行。

image.png

那如何讓 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 更能節省空間。

image.png
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 建立就不講了。

image.png
以 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


  1. https://www.nuget.org/
  2. https://docs.microsoft.com/zh...
  3. https://docs.microsoft.com/zh...
  4. https://webassembly.org/
  5. https://docs.microsoft.com/zh...
  6. https://github.com/dotnet/run...
  7. https://github.com/dotnet/run...
  8. https://dot.net
  9. https://github.com/dotnet/cli...
  10. https://github.com/google/brotli
  11. https://caniuse.com/?search=W...
  12. https://docs.microsoft.com/zh...
  13. https://docs.microsoft.com/zh...
  14. https://github.com/mingyaulee...
  15. https://github.com/AdrienTorr...
  16. https://github.com/cnryb/blaz...

相關文章