最近在学习 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组件,因为两者可能起冲突。

标签: none

添加新评论