.NET8 Blazor 從入門到精通:(一)關鍵概念

二次元攻城狮發表於2024-08-02

目錄
  • Blazor 的關鍵概念
    • 專案模板
    • Razor 語法
    • 依賴注入
      • 注入配置
    • HeadOutlet 元件
    • @code 分離
    • Blazor 除錯
    • CSS 隔離
    • 呼叫JavaScript

最近在學習 Blazor ,在B站上找了一個國外的課程邊看邊學習。嗯,原價¥1503的課程,大概200多美元,課程連結如下:

B站(大章節分P-適合初學):.NET 8 Blazor 從入門到精通

B站(小章節分P-適合複習):Blazor從入門到精通(中文字幕)

官網課程:Blazor From Start to Finish

image

Blazor 的關鍵概念

本文主要介紹Blazor 的關鍵概念,每個知識點都附上了學習過程中查到的參考資料。文中刪除了一些常識性或表述不清的內容,如熱過載、元件與頁面等。

專案模板

專案開發的常用模板配置項如下,其它配置也可以都試一下,觀察一下區別:
image

Auto 互動方式:最初使用 Blazor Server,並在隨後訪問時使用 WebAssembly 自動進行互動式客戶端呈現,詳細內容參考.NET8 Blazor的Auto渲染模式的初體驗

Razor 語法

參考 ASP.NET Core 的 Razor 語法參考,前期主要理解下面幾個重點語法即可:

  • 隱式 Razor 表示式:以 @ 開頭,後跟 C# 程式碼
<p>@DateTime.Now</p>
<p>@DateTime.IsLeapYear(2016)</p>
  • 顯式 Razor 表示式:由 @ 符號和圓括號組成
<p>Last week this time: @(DateTime.Now - TimeSpan.FromDays(7))</p>
  • @code 塊:允許 Razor 元件將 C# 成員(欄位、屬性和方法)新增到元件
@code {
    // C# members (fields, properties, and methods)
}
  • 迴圈語句和條件語句:如 @for@if 等,直接寫在頁面中
@for (var i = 0; i < people.Length; i++)
{
    var person = people[i];
    <p>Name: @person.Name</p>
    <p>Age: @person.Age</p>
}

@if (value % 2 == 0)
{
    <p>The value was even.</p>
}

依賴注入

參考 將依賴項注入 Blazor 元件Program.cs(專案載入程式) 中註冊依賴項:

builder.Services.AddSingleton<DemoDependency>();
//用於註冊依賴項的其他模式...

對於 Blazor 元件,有兩種方法可以指示我們的元件使用哪些依賴項:

//1.在 Razor 標記中
@inject IToDoApi ToDoApi
@inject ISomeServiceType AnotherService

//2.在 C# 程式碼中
@code
{
  [Inject]
  private IYetAnotherServiceType PropertyInjectedDependency { get; set; }
}

注入配置

參考 ASP.NET Core Blazor 配置 ,其中配置的優先順序別:使用者機密 > appsettings.{Environment}.json > appsettings.json

在 appsettings.json 中配置連線字串:

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*",
  "ConnectionStrings": {
    "Default": "連線字串來自appsettings.json"
  }
}

在元件中引入配置依賴:

@page "/"
@inject IConfiguration config

<PageTitle>Home</PageTitle>

<h1>Hello, world!</h1>
<h2>@config.GetConnectionString("Default")</h2>

IConfiguration 是預設註冊的,不需要另外寫程式碼註冊,可以直接使用。

HeadOutlet 元件

切換頁面時不是整個頁面被重新載入,實際上只有根元件 App.razor<Routes /> 被重新渲染。這種渲染方式不利於SEO,可以使用 HeadOutlet 元件來控制 <head> 元素的內容來進行最佳化。

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <base href="/" />
    <link rel="stylesheet" href="bootstrap/bootstrap.min.css" />
    <link rel="stylesheet" href="app.css" />
    <link rel="stylesheet" href="KeyConcepts.styles.css" />
    <link rel="icon" type="image/png" href="favicon.png" />
    <HeadOutlet />
</head>

<body>
    <Routes />
    <script src="_framework/blazor.web.js"></script>
</body>

</html>

參考 在 ASP.NET Core Blazor 應用中控制 <head> 內容,指定一個頁面的標題和描述:

@page "/control-head-content"

<PageTitle>@title</PageTitle>
<p>Title: @title</p>
<p>Description: @description</p>

<HeadContent>
    <meta name="description" content="@description">
</HeadContent>

@code {
    private string description = "This description is set by the component.";
    private string title = "Control <head> Content";
}
  • 使用 PageTitle 元件指定頁面標題,這樣可以將 HTML <title> 元素呈現給 HeadOutlet 元件。
  • 使用 HeadContent 元件指定 <head> 元素內容,該元件為 HeadOutlet 元件提供內容。

需要注意,如果在A頁面用了B頁面,那麼B頁面的 PageTitle 會覆蓋掉A頁面的 PageTitle。所以,元件不需要作為頁面使用時就不要放 PageTitle 了。

@code 分離

Blazor可以支援在razor檔案裡面新增cs程式碼,但是程式碼一旦複雜了之後就會變得特別的麻煩。其實,這部分程式碼在編譯時實際是被分離出來的,我們也可以在編譯前手動將它們分離出來。

右鍵 code ,選擇 快速操作和重構 ,然後如下圖所示選擇 將塊提取到程式碼隱藏中
image

結果如下,其中①只是②的一個快捷方式:
image

上面是使用VS的自動分離功能,也可以使用手動的方式進行分離。參考 C# Blazor 學習筆記(4):blazor程式碼分離,注意以下幾點:

  • 直接右鍵razor元件的上級目錄,新增一個partial區域性類
  • 新建類的類名是xxx.razor.cs,這樣才能掛到元件上面
xxx.razor
xxx.razor.cs:程式碼
xxx.razor.css:css樣式

程式碼分離後,依賴項也需要改成屬性注入:

using Microsoft.AspNetCore.Components;

namespace KeyConcepts.Client.Pages;

public partial class Demo
{
    // 在razor元件中是這樣的 @inject IConfiguration config
    [Inject]
    protected IConfiguration config { get; set; }=default!;

    private string? GetConnectionString()
    {
        return config.GetConnectionString("Default");
    }
}

Blazor 除錯

除錯沒什麼好說的,就在VS中正常打斷點、單步執行、監控變數值就行了,具體參考 除錯 ASP.NET Core 應用

CSS 隔離

CSS 隔離可以將 CSS 範圍限定到 Razor 元件,以簡化 CSS 並避免與其他元件或庫發生衝突,但過多的使用也會導致 CSS 追蹤困難。

參考 ASP.NET Core Blazor CSS 隔離 ,在與元件相同資料夾中建立一個 .razor.css 檔案,該檔案與元件的 .razor 檔案的名稱相匹配。例如為 Counter.razor 元件建立一個 Counter.razor.css 檔案:

h1 {
    color:red;
}

生成時 Blazor 會重寫 CSS 選擇器以匹配元件呈現的標記, 重寫的 CSS 樣式被作為靜態資產捆綁和生成, 預設情況下在 <head> 標記中引用表樣式:

<!-- {ASSEMBLY NAME} 佔位符是專案的程式集名稱 !-->
<link href="{ASSEMBLY NAME}.styles.css" rel="stylesheet">

在捆綁的檔案中,每個元件都與範圍識別符號關聯。 對於每個具有樣式的元件,HTML 屬性追加有格式 b-{STRING},其中 {STRING} 佔位符是框架生成的十個字元的字串。 識別符號對每個應用都是唯一的。

在呈現的 Counter 元件中,Blazor 將範圍識別符號追加到 h1 元素:

<h1 b-zdeg3nv67a="">Counter</h1>

image

注:如果CSS不生效,需要清理一下瀏覽器的快取。

呼叫JavaScript

js檔案可以放到wwwroot目錄下,也可以關聯到特定元件,參考
從與元件並置的外部 JavaScript 檔案 (.js) 載入指令碼 為 Counter 元件新增並置js檔案:

//Counter.razor.js
export function displayCount(count) {
    alert('The count is' + count);
}

export function createMessage(count) {
    return 'The count is' + count;
}

Blazor 應用的 Razor 元件使用 .razor.js 副檔名並置 JS 檔案(參考 CSS 隔離部分),並且可透過專案中檔案的路徑公開定址 {PATH}/{COMPONENT}.razor.js

  • 佔位符 {PATH} 是指向元件的路徑
  • 佔位符 {COMPONENT} 是元件

修改 Counter 元件的程式碼,呼叫js函式:

@page "/counter"
@rendermode InteractiveAuto
@inject IJSRuntime JSRuntime

<PageTitle>Counter</PageTitle>

<h1>Counter</h1>
<h2>@subMessage</h2>
<p role="status">Current count: @currentCount</p>

<button class="btn btn-primary"  @onclick="IncrementCount">Click me</button>

@code {
    private int currentCount = 0;
    private string subMessage = "";
    private IJSObjectReference? jsModule;

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            jsModule = await JSRuntime.InvokeAsync<IJSObjectReference>("import", "./Pages/Counter.razor.js");
        }    
    }
    private async Task IncrementCount()
    {
        currentCount++;
        await jsModule.InvokeVoidAsync("displayCount", currentCount);
        subMessage = await jsModule.InvokeAsync<string>("createMessage", currentCount);
    }
}
  • @inject IJSRuntime JSRuntime:注入 IJSRuntime 介面,用於與客戶端 JavaScript 互動
  • IJSObjectReference? jsModule:儲存對 JavaScript 模組的引用
  • JSRuntime.InvokeAsync
    <IJSObjectReference>
    :載入 JavaScript 模組並儲存其引用

實際專案中,儘量不要使用js控制DOM,而是使用Blazor元件,因為兩者可能起衝突。

相關文章