ASP.NET Blazor 關鍵報告

前言

ASP.NET Blazor (以下簡稱為 Blazor) 是微軟使用 WebAssembly 技術而發展出的全新 Web 網頁開發平台,雖然基於 .NET Core Runtime 與 ASP.NET Core 的框架,但股子裡與 ASP.NET Core 截然不同,因為他雖然基於 .NET Core 平台 與 C# 語言,但他實際執行時,將 .NET 編譯的最後結果 + mscorlib 運行在 mono Runtime (mono.wasm)上,而它也是一個 SPA 框架,在 ASP.NET Core 2.1 推出時,以新增功能方式推出,但它的完整性未來可望擴充為獨立、完整的 Web 開發框架,由於透過 Web Assembly 技術而發展而來,也就是說,幾乎只要 browser 支援標準的 Web Assembly,您所開發的應用程式幾乎可以無縫執行在任一個平台之中。


ASP.NET Blazor 的版本

撰寫這一篇時,版本已經演進到 0.9.0,不過我剛剛在安裝 Visual Studio 2019 的 Project Templates 時,居然出現了 404 Not Found 的狀況,猜測應該正好在更新的關係,看來我運氣還真好,不惑還好,稍後個半小時後,現在又可以安裝了。

下載單一 VSIX,安裝檔可從此連結下載:

https://marketplace.visualstudio.com/items?itemName=aspnet.blazor


ASP.NET Blazor 的三種樣板: 

有別於在 0.6.0 所提供的『Standalone』、『ASP.NET Core Host』、『Server-Side in ASP.NET Core』,的三種樣板,若沒記錯,從 0.8.0 新增的 Razor Components 並修改為如下:

(1). Blazor (hosted in ASP.NET server)

將原本的『ASP.NET Core Host』、『Server-Side in ASP.NET Core』合而為一,提供前後端分離+具備 Controller + Host 主機的 Blazor SPA 應用程式,Client 與 Server 透過 SignalR 進行溝通處理。

(2). Blazor Library

在 0.8.0 開始,新增了 Razor Components,特點是將 UI 的邏輯與實際的 UI 隔離,使用 C# 而不是 JavaScript 來撰寫互動式 UI,在 .NET Core 3.0 裡,Razor Component 可以在 ASP.NET Core 應用程式裡,支援以將 Razor Component 裝載在伺服器上,UI 更新透過 SignalR 連線進行處理。


(3). Blazor (standalone)

一樣是純 SPA 的應用程式,這與 0.6.0 的相同並沒有改變。


所需環境

(1). Visual Studio 2019 Preview 4 or Later

筆者使用 Visual Studio 2019 Preview 4.1 SVC1

(2). .NET Core 3.0 SDK 3.0.100-preview3-010431

https://dotnet.microsoft.com/download/dotnet-core/3.0

(3). 安裝 Project Template (Blazor extensions on Visual Studio Marketplace)

安裝:Visual Studio 2019 Project Templates (Microsoft.AspNetCore.Blazor.Templates)

https://marketplace.visualstudio.com/items?itemName=aspnet.blazor

為什麼要裝 C# Project Template 呢?而事實上,C# Project Templates 只是讓你可以透過 Visual Studio 的【新增專案】的 UI 功能以滑鼠點選方式來建立 Blazor (後面都簡稱為 Blazor) 專案,如果您不一定在 Windows 中開發 Blazor,那麼筆者會建議使用 dotnet CLI 來建立專案會比較方便。

(4). 安裝 dotnet CLI 的 Blazor Template.

要使用 dotnet CLI 來建立 Blazor 您也得安裝 CLI 範本

安裝 CLI 的跨平台範本,請開啟命令列中具並輸入下面命令:

dotnet new -i Microsoft.AspNetCore.Blazor.Templates::0.9.0-preview3-19154-02

註:dotnet CLI 現在已經進化為各平台的 Shared SDK Components 了,因此會確保開發人員不管在哪一個平台執行 CLI 命令結果都相同、命令、參數操作方式都完全相同(dotnet CLI is run anyware)!

注意:Visual Studio 2017 不支援 .NET Core 3.0 類型專案與 .NET Standard 2.1 類型專案


細部探討 Blazor 的前世今生

為什麼會有 Blazor 的誕生?這又得從 2015 的時候,由 Google、微軟、與 Mozilla 加上 Apple 所發起的 WebAssembly 計畫開始談起,當時他們計畫設計出一種將 JavaScript 預先編譯成二進制碼,而讓這個二進制碼可以直接在瀏覽器種執行,好處在於預先編譯可以將 bytecode 大小縮至最小,好比純文字檔案 vs. DLL 之間,當然是編譯成 DLL 檔案最小,再者,有鑑於現有 JavaScript 的引擎並不是預先編譯,因為 JavaScript 本身是種『腳本語言』,由內建於 browser 的 JavaScript 解釋器,再與呈現引擎,又稱渲染引擎,也被稱為瀏覽器內核部分,又稱為 UI 線程,它是由各大瀏覽器廠商依照 W3C 標準自行研發的,常見的瀏覽器內核可以分這四種:Trident、Gecko、Blink、Webkit 等等,如 Chrome 就是使用 Webkit 內核,包括 Safari 也是使用 Webkit,而這扯太遠了不是本篇的重點XDD,另外,因為現有的 JavaScript 已經非常壯大,社群支援/Third-party 第三方支援可說是非常完整,JavaScript 也強大到足以解決所有問題(透過 node.js 也可開發伺服器端),但是,當我們使用 JavaScript 進行撰寫一些 3D 遊戲、虛擬實境的時候,幾萬行的 JavaScript 常讓開發人員望之卻步,而 WebAssembly 就是要來解決這樣的問題。

再來是執行效率問題,前面提到受限於 JavaScript 的編譯模型 與 browser 內核影響+包袱,現有 JavaScript 的執行速度已經達瓶頸,未來不太會有提升空間,而透過 WebAssembly 預先將 JavaScript 預先編譯成二進制碼,而二進制碼已經是種機械碼,所以它的執行速率幾乎是原有 JavaScript 的 20 倍左右,在這個標準剛推出之前,WebAssembly 只能使用 C++ 或 assembly 進行開發,但各大廠是希望 WebAssembly 成為公開的標準,也就是說,任何語言、平台,只要能將你的高階語言編譯成 WebAssembly,就能在瀏覽器中執行。

但是 WebAssembly 並不是要取代現有的 JavaScript,尤其現有 JavaScript 生態已趨完整,有許多不可取代性,而 WebAssembly 則是希望補其不足的地方,因為 WebAssembly 仍然需要 JavaScript。

Blazor 誕生:

因為 WebAssembly 而促使了 Blazor 的誕生,Blazor 的創始者是 Steve Sanderson 所創造,而這本來是 將從Steve Sanderson 的個人專案,後來被 ASP.NET 開發團隊納入為 ASP.NET 官方支援的專案,而 Steve Sanderson 表示,Blazor 是取 【Browser + Razor Pages】取字首的縮寫而成,當初創造 Blazor 是看到現有 JavaScript 在開發時,有許多的 Front-End Framework 的支持,像是 Angular、React、Vue 等等,而 .NET 的開發者而無法獲取這些好處,即便 .NET 也可開發前後端應用程式,但卻不像 JavaScript 有那麼多框架支援,於是就基於 WebAssembly 之上,開發出一種讓 .NET 的開發者使用原本熟悉的 C# + Razor 與 HTML 來開發瀏覽器上的單一頁面應用程式(SPA, Single Page Application),所以 Blazor 是希望將現有的 .NET 的生態體系都成為 Blazor 龐大的框架 Framework 與支援,就像 Front-End Framework 一樣。

WebAssembly 的運作情形:

傳統的 JavaScript 經過解譯器解譯後,編譯、最後才交給 JIT Interpreter 來執行。

圖片來源:https://www.youtube.com/watch?v=PiJtEZYMxOc

因為 JavaScript 如上方所提到,JavaScript 會先經過『解譯器』的解析,也就是上圖中的 Parser 的處哩, Compiler 會將 JavaScript 編譯成二進制碼,而 WebAssembly 試想直接透過工具的支援,預先先編譯成二進制碼,並直接交由 JIT Interpreter 來執行,也記說,理論上,個語言,各開發工具編譯器如果能夠將自身的高階語言預先編譯成 JIT Interpreter 可接受的二進制碼,那麼就可以省去了 browser 的 Parser 與 Compiler 的過程,自然省去不少時間。


Blazor 的運作情形:

圖片來源:https://www.youtube.com/watch?v=PiJtEZYMxOc

與一般 JavaScript + HTML 所撰寫的 SPA 不同之處在於,除了前端使用 C# + Razor + HTML 所撰寫外,Blazor 將其編譯結果下載到前端,也就是 Browser 中執行,但我們知道,由於 .NET 是基於 CLR (Common Language Runtime) 為執行環境,所有 NET 的 (Assembly)或稱作組件,即便 Compiler 為 .DLLs 之後,還會有一個 JIT 的過程,將其編譯成特定平台的二進制碼。由上圖可以很明顯的發現,前面編譯成 .DLLs 的過程已經在設計時期 Visual Studio 中完成了,所以 Blazor 會將所有 .NET 的編譯結果,也就是整個 ASP.NET Core 網站的編譯結果包括:mono runtime (mono.wasm)下載至前端 browser 中執行,而為什麼需要 mono Runtime?因為 .NET 編譯出來的 .DLLs 是 IL (Intermediate Language) 中介語言,這是一個無關平台/CPU 的語言,所以 .NET 還需要自己的 JIT 將其再次編譯成目的機器 CPU 的二進制碼,在 Windows 上有 .NET 1.0/1.1/2.0/4.0 CLR,跨平台則有 .NET CoreCLR,但在這裡就得借助 mono Runtime 的幫忙了,將 .NET 相關 DLLs 載入編譯成 JavaScript Runtime 的 JIT Interpreter 能夠看得懂的二進制碼。


關於 Razor Component 詳細解說

在 0.6.0 版本的就將 .cshtml 當作一個網頁組件來看,而且在官方的範本裡,Index.cshtml 就內嵌著 <SurveyPrompt> 的 cshtml 頁面。

我們打開 SurveyPrompt 的檢視內容

在 SurveyPrompt 可以看見連屬性都元件化,只需要在 Razor functions 裡撰寫 C# 的方式,將要公開的屬性撰寫上去,並加上 [Parameter] 的 Attributes 即可。

同理,我們如果要撰寫自定義的 View 檢視頁面,只要在 Shared 資料夾裡加入【Razor 檢視】

注意:這裡必須加入『Razor 檢視』而不是『Razor 頁面』因為 Razor 頁面並不相容於 Blazor,Blazor 所使用的.NET Core 3.0 版本的 Razor Page,有一些語言特性是 ASP.NET Core 2.1/2.2 的 Razor Page 所沒有的。

想測試輸出結果,在 Index 檢視頁面上輸入剛才新增的 MyDemoPage 的檢視名稱,會發現 IntelliSense 自動帶出來了

而且還動態的幫我們 Compiler 成 ComponentBase 類型,這有一點像是 TagHelper,但又不完全是,這是在背景作業中完成的。如果,你把編譯出來的 WebBlazor30TestApp2.Client.dll 直接 Decompile 可以看到,所有在 Pages、Shared 資料夾下的所有 Razor Pages 都被編譯成衍伸於 ComponentBase 類型的類別

除了 MainLayout Page 之外,因為 MainLayout 是延伸於 LayoutComponentBase 類別,不過您仔細一看,會發現,原來 LayoutComponentBase 類別也是延伸於 ComponentBase 類別


偵錯 Blazor 應用程式

針對偵錯 Blazor 應用程式,即便到了 0.9.0 如果需要偵錯 Client App (WebAssembly) 仍然還是透過 Chrome DevTools Protocol 協定來進行,這也是因為 Blazor 是將 .DLLs 也就是 .NET Assembly 所有相關的輸出檔(通常是DLL) + mono.wasm (Mono Runtime CLR)執行環境全部下載至 browser 端來執行,在 browser 端,如果要 Debug ,勢必能夠 Decompile .NET DLL 的能力,但是,想也知道,訪間各 browser 怎麼可能會有 .NET 的 decompile 的能力,無法 decompile 怎麼查看 sources?更不要說還要 Trace,而 Trace 也要能夠查看變數的內容,這.. 實在太麻煩了。所以目前也還是藉由 Chrome DevTools Protocol 協定來進行。

Browser sources Map:

Chrome 的 DevTools Protocol 其實也是種 Remote debugging 的協定,由於 Blazor 是在前端的瀏覽器 browser 中,在 JavaScript 的 JIT Interpreter 執行時產生的 IL (Intermediate Language) 映射到 Chrome 的 Debug Tools 裡來達到 Trace 的效果,只是因為是映射,所以就像偵錯 JavaScript 一樣,但是 CLR 在這裡是無法查看變數內容的,因為變數內容在 CLR 環境的 Call Stack 裡,這裡只是映射出 Script,但 CLR 內是受保護的,所以沒有辦法對應出變數值得內容,也就是說,你只能 Trace,但是無法查看變數值的內容。

另外,要偵錯 Blazor 的應用程式,有一麼眉角需要注意,這也是第一次偵錯的開發者最容易犯的錯誤,會以為 Chrome Remote Debugging 怎麼沒有作用?

(1). 只以 Visual Studio 的偵錯方式啟動應用程式是看不見 Assembly 名稱的

如果您是像我一樣,一開始直覺的在 Visual Studio 2019 裡,以 F5 偵錯模式啟動 Blazor 應用程式,那當你在這個由 Visual Studio 啟動的 chrome 裡按下 Shift+Alt+D 時,是無法在 Chrome Debugging Tools 裡找到可偵錯的 [YourProjectName].Client.DLL & [YourProjectName].Shared.DLL(這裡以 Hosted in Server 為例)

如果,你在這樣的 Context 環境下,直接剪貼如上圖的命令列,Chrome 也只會多開一個 Tab 來服務你的需求,但仍然在 Visual Studio 的偵錯模式下,在這樣的模式下如果你又按下 F12 ,那麼這只是一個偵錯 JavaScript 的前端 DevTools 而已,是看不到任何的 Assemblies 的。


(2). 獨立以命令列啟動 Chrome

正確的使用 Chrome Remote Debugging 的方式應該是直接以命令列啟動 Chrome 即可!並在這樣的 Context 環境下按 Shift + Alt + D,接著,這時 Chrome 才會自己打開偵錯工具 DevTools (並非按 F12),這時的 Debugging Tools 就會出現可偵錯的 [YourProjectName].Client.DLL & [YourProjectName].Shared.DLL(這裡以 Hosted in Server 為例)


JavaScript Interop

在目前釋出的 Blazor 若需要在 Razor 語法裡面調用 JavaScript 的話,而呼叫的方式從 0.9.0 起,取消了 JSRuntime.Current 的方式,改由 DI 注入的方式可在 Razor Page 上方注入 IJSRuntime,如下:

接著,在 Razor 內只需要使用如下:


如果要從 JavaScript 裡呼叫 .NET 方的話,.NET 的方法必須是靜態的方法,當然,這個方法通常是撰寫在 Razor Page 上的 functions {},而且還必須冠上 [JSInvokable] 的 NET Attributes 如下:

呼叫方式如下:


對 Blazor 開發有興趣的朋友有福了,剛好我最近也推出了 Blazor 相關課程,目前只要 $2690

課程放置在 HiSKIO 平台、用我的連結購買我才會分潤比較多唷!感恩~

待續…..


ASP.NET Blazor 系列文章導讀

(一)、ASP.NET Blazor 關鍵報告

(二)、Blazor 怎麼處裡狀態?

(三)、Blazor 怎麼處裡狀態?【保存預設範本的 Count 值】

(四)、Blazor 的 CRUD 應用程式 – Use Entity Framework Core

(五)、Blazor 如何使用自定義的 ApiHostBase?【接續 Clean Architecture 的 API 框架設計 (1)】

(六)、Blazor 如何使用自定義的 ApiHostBase?【接續 Clean Architecture 的 API 框架設計 (2)】

(七)、… 還在想 XDD


參考資料:

https://devblogs.microsoft.com/aspnet/blazor-0-9-0-experimental-release-now-available/

https://docs.microsoft.com/zh-tw/aspnet/core/mvc/views/razor?view=aspnetcore-3.0

https://zhuanlan.zhihu.com/p/32751855




關於 Gelis:

資深 .NET 技術顧問

FB 社團 (軟體開發之路):

https://www.facebook.com/groups/361804473860062/

FB 粉絲團 (Gelis 的程式設計訓練營):

https://www.facebook.com/gelis.dev.learning/

我講授過的課程 SlideShare:

https://www.slideshare.net/GelisWu


以下是我經營的項目與內容:

(1). 企業內訓課程

(2). 專業顧問


企業內訓課程:

1. .NET Core 3.1 從入門到進階

先前實體課程連結

2. 跨平台的 Web API Framework 框架設計

先前實體課程連結

3. 決戰 OOAD 系列課程 - 使用 UML

先前實體課程連結

4. 單元測試 UnitTest 與 Moq 物件實務課程

先前實體課程連結

5. 快速開發系列 - C# Project Templates 範本設計

留言

  1. 這樣的做法跟Silverlight很類似,差別是以前用XAML做前端UI,現在改HTML降低門檻。

    回覆刪除
    回覆
    1. 您好,是有些像沒有錯,但是仔細想想會發現,其實與 Silverlight 還是有稍稍不同,不同在於 Assembly 所有瀏覽器均內建,且得到所有大廠的支援(因為國際OMG組織全力發展的)。

      而 Silverlight 為單一大廠微軟獨自發展而又得要安裝Plug-In 插件 自然容易會有安全性議題+也容易讓人認為『標準不易統一』等問題。

      舉個最實際的例子:我是 A 大廠,我為什麼要支援你 M 大廠的插件?那未來我在上面 (Platform) 的軟件的發展不就受限於你?XD

      因為對於這一類 Plug-In 或 Assembly 等於一個商用應用軟體執行的 Platform

      刪除
  2. 謝謝您!我會更加努力更新 Blog 的。

    回覆刪除

張貼留言

這個網誌中的熱門文章

什麼是 gRPC ?

什麼是 gRPC(二)- 來撰寫第一個 Hello World 吧!

常見的程式碼壞味道(Code Smell or Bad Smell)