Skip to content

使用 Blazor 构建 Web 应用

了解 Blazor

Blazor 是开发人员可以通过编写 C# 代码来创建丰富的交互式用户界面(UI)的框架。 使用 Blazor,可以为所有代码(服务器端和客户端)使用相同的语言。 可以渲染它以在多种不同的浏览器中显示,包括移动设备上的浏览器。

TIP

Blazor 应用中的代码有两个托管模型:

  • Blazor 服务器:在此模型中,应用在 ASP.NET Core 应用中的 Web 服务器上执行。 在客户端上,UI 更新、事件和 JavaScript 调用通过客户端和服务器之间的 SignalR 连接发送。 在本模块中,我们将讨论此模型并编写代码。

  • Blazor WebAssembly:在此模型中,下载 Blazor 应用及其依赖项,并在浏览器中运行 .NET 运行时。

在 Blazor 中,UI 是从称为组件的自包含代码部分构建的。 每个组件可以包含 HTML 和 C# 代码的组合。 组件是使用 Razor 语法编写的,其中代码使用 @code 指令进行标记。 其他指令可用于访问变量、绑定到值并实现其他呈现任务。 编译应用后,HTML 和代码将编译为组件类。 组件被写入具有 .razor 扩展名的文件中。

TIP

Razor 语法用于将 .NET 代码嵌入网页。 可以在 ASP.NET MVC(模型-视图-控制器)应用程序中使用它,其中的文件具有 .cshtml 扩展名。 Blazor 中使用 Razor 语法编写组件。 这些组件反而具有 .razor 扩展,控制器和视图之间没有严格的分隔。

使用 Blazor 构建你的第一个 Web 应用

创建并运行 Blazor Web 应用

使用 .NET CLI 创建新的 Blazor 应用

sh
$ dotnet new blazor -o BlazorApp
已成功创建模板“Blazor Web 应用”。
此模板包含除 Microsoft 以外其他方的技术,请参阅 https://aka.ms/aspnetcore/9.0-third-party-notices 以获取详细信息。

正在处理创建后操作...
正在还原 D:\dotnet-training\BlazorApp\BlazorApp.csproj:
已成功还原。

默认组件包括 Index.razor 主页和 Counter.razor 演示组件。 这两个组件都放置在 Pages 文件夹中。 可以修改这些视图以满足你的需求,或将其删除,并将其替换为新组件。

Blazor 项目结构

生成的项目都包含以下文件和页面:

  • Program.cs 是启动服务器和配置应用服务和中间件的应用的入口点。
  • App.razor 是应用的根组件。
  • Routes.razor 配置 Blazor 路由器。
  • Components/Pages 目录包含应用的一些示例网页。
  • BlazorApp.csproj 定义应用项目及其依赖项,可以通过双击解决方案资源管理器中的项目节点来查看。
  • Properties 目录中的 launchSettings.json 文件为本地开发环境定义不同的配置文件设置。 在创建项目时自动分配端口号,并将其保存在此文件上。

使用 .NET CLI 运行应用

sh
$ dotnet watch
dotnet watch 🔥 Hot reload enabled. For a list of supported edits, see https://aka.ms/dotnet/hot-reload.
  💡 Press "Ctrl + R" to restart.
dotnet watch Building D:\dotnet-training\BlazorApp\BlazorApp.csproj ...
dotnet watch 🔨 Build succeeded: D:\dotnet-training\BlazorApp\BlazorApp.csproj
 D:\dotnet-training\BlazorApp\Properties\launchSettings.json 使用启动设置...
info: Microsoft.Hosting.Lifetime[14]
      Now listening on: http://localhost:5104
info: Microsoft.Hosting.Lifetime[0]
      Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
      Hosting environment: Development
info: Microsoft.Hosting.Lifetime[0]
      Content root path: D:\dotnet-training\BlazorApp
warn: Microsoft.AspNetCore.HttpsPolicy.HttpsRedirectionMiddleware[3]
      Failed to determine the https port for redirect.

已成功运行第一个 Blazor 应用!

Razor 组件

设置开发环境后,让我们探索 Blazor 项目的结构,并了解 Blazor 组件的工作原理。

应用的主页由位于 Components/Pages 目录中的 Home.razor 文件定义。 Home.razor 包含以下代码:

razor
@page "/"

<PageTitle>Home</PageTitle>

<h1>Hello, world!</h1>

Welcome to your new app.

@page 指令指定了此页面的路径,因此当用户导航到应用的根目录时,Home 组件将被显示。 该 PageTitle 标记是一个 Blazor 组件,用于设置当前页的标题,使其显示在浏览器选项卡中。文件的其余部分是定义页面内容的普通 HTML。

什么是 Razor?

Razor 是基于 HTML 和 C# 的标记语法。 Razor 文件 (.razor) 包含纯 HTML,然后包含 C# 来定义任何呈现逻辑,例如条件、控制流和表达式计算。 然后,Razor 文件编译为封装组件呈现逻辑的 C# 类。

什么是 Razor 组件?

如果浏览 Blazor 项目中的文件,你会注意到构成项目的大多数文件都是 .razor 文件。 在 Blazor 中,Razor 文件定义构成应用 UI 部分的可重用组件。 组件定义要呈现的 HTML 以及如何处理用户事件。

在编译时,每个 Razor 组件都内置在 C# 类中。 该类可以包含常见的 UI 元素,例如状态、呈现逻辑、生命周期方法和事件处理程序。 由于在 Razor 中创建的 Blazor 组件只是 C# 类,因此可以使用组件的任意 .NET 代码。

  • 使用组件

若要使用另一个组件中的组件,请添加一个 HTML 样式标记,其名称与组件名称匹配。 例如,如果你有一个名为 MyButton.razor 的组件,你可以通过添加 <MyButton /> 标签将 MyButton 组件添加到另一个组件。

  • 组件参数

组件还可以具有参数,允许在使用组件时将数据传递到组件。 组件参数通过向具有 [Parameter] 属性的组件添加公共 C# 属性来定义。 然后,可以使用与属性名称匹配的 HTML 样式属性为组件参数指定值。 参数的值可以是任何 C# 表达式。

  • @code

Razor 文件中的 @code 块用于将 C# 类成员(字段、属性和方法)添加到组件。 可以使用 @code 来跟踪组件状态,添加组件参数,实现组件生命周期事件,并定义事件处理程序。

尝试使用 Counter 组件

在正在运行的应用中,选择左侧边栏中的“Counter”选项卡导航到计数器页面。 随后应会显示以下页面:

counter

(图片来源:自己截的)

选择“Click me”按钮,在不刷新页面的情况下递增计数。 在网页中递增计数器通常需要编写 JavaScript,但使用 Blazor 时,可以使用 C#。

可在 Components/Pages/Counter.razor 处找到 Counter 组件的实现。

浏览器中针对 /counter 的请求(由顶部的 @page 指令指定)会导致 Counter 组件呈现其内容。 该 @rendermode 指令为组件启用交互式服务器呈现,以便它可以处理来自浏览器的用户界面事件。

每次选择“单击我”按钮时会出现以下情况:

onclick 事件被触发。 调用 IncrementCount 方法。 currentCount 递增。 组件被渲染以显示更新后的计数。

添加组件

向主页添加 Counter 组件

  1. 打开 Components/Pages/Home.razor 文件。

  2. 通过在 Home.razor 文件的末尾添加 <Counter /> 元素,向页面添加 Counter 组件。

razor
@page "/"

<PageTitle>Home</PageTitle>

<h1>Hello, world!</h1>

Welcome to your new app.

<Counter />
  1. 通过重启应用或使用热重载来应用更改。 组件 Counter 显示在主页上。

counter-homepage

(图片来源:自己截的)

修改组件

修改主页的 Counter 组件

定义组件上的 Counter 参数,以指定每次单击按钮时递增多少。

  1. IncrementAmount 添加具有 [Parameter] 特性的公共属性。

  2. IncrementCount方法更改为在递增IncrementAmount的值时使用currentCount的值。

Counter.razor 中更新的代码应如下所示:

razor
@page "/counter"
@rendermode InteractiveServer

<PageTitle>Counter</PageTitle>

<h1>Counter</h1>

<p role="status">Current count: @currentCount</p>

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

@code {
    [Parameter]
    public int IncrementAmount { get; set; } = 1;

    private int currentCount = 0;

    private void IncrementCount()
    {
        currentCount += IncrementAmount;
    }
}
  1. Home.razor 中,更新 元素来添加一个 IncrementAmount 属性,它会将增量更改为 10,如以下代码中的最后一行所示:
razor
@page "/"

<PageTitle>Home</PageTitle>

<h1>Hello, world!</h1>

Welcome to your new app.

<Counter IncrementAmount="10" />

将更改应用到正在运行的应用。

Home 组件现在有自己的计数器,每次选择“Click me”按钮时,计数器值都递增 10,如下图所示。

counter-homepage-modified

(图片来源:自己截的)

/counter 的 Counter 组件继续增加 1。

使用 Blazor 构建待办事项列表

Razor 指令

Razor 指令是 Razor 语法中的保留关键字,会影响 Razor 文件的编译方式。 Razor 指令始终以 @ 字符开头。 某些 Razor 指令出现在新行的开头,例如 @page@code,而其他指令则是可以作为属性应用于元素的属性,例如 @bind。 可以在 Razor 语法参考中找到 Razor 指令的完整列表。

数据绑定和事件

呈现 C# 表达式值

若要在 Razor 中呈现 C# 表达式的值,可以使用前导 @ 字符。 例如,Counter 组件可以呈现其 currentCount 字段的值,如下所示:

razor
<p role="status">Current count: @currentCount</p>

Razor 通常可以判断出 C# 表达式何时结束,以及你何时转换回编写 HTML。 但是,也可以使用圆括号()来明确表达式的开始和结束。

razor
<p role="status">Current count: @(currentCount)</p>

添加控制结构

可以使用 C# if 语句有条件地呈现某些内容,如下所示:

razor
@if (currentCount > 3)
{
    <p>You win!</p>
}

还可以使用 C# 循环访问数据并呈现项列表:

razor
<ul>
    @foreach (var item in items)
    {
        <li>@item.Name</li>
    }
</ul>

处理事件

Blazor 组件通常处理 UI 事件。 若要从 UI 元素为事件指定事件回调,可以使用以 @on 开头且以事件名称结尾的属性。 例如,可以使用 IncrementCount 属性将 @onclick 方法指定为按钮单击事件的处理程序,如下所示:

razor
<ul>
    @foreach (var item in items)
    {
        <li>@item.Name</li>
    }
</ul>

还可以为其他 HTML 事件指定 C# 事件处理程序,例如 @onchange@oninput 等。 事件处理方法可以是同步的,也可以是异步的。 还可以使用 C# Lambda 表达式内联定义事件处理程序:

razor
<button class="btn btn-primary" @onclick="() => currentCount++">Click me</button>

事件处理程序方法可以选择采用包含事件相关信息的事件参数。 例如,你可以访问已更改的输入元素的值,如下所示:

razor
<input @onchange="InputChanged" />
<p>@message</p>

@code {
    string message = "";

    void InputChanged(ChangeEventArgs e)
    {
        message = (string)e.Value;
    }
}

运行事件处理程序后,Blazor 将自动呈现具有新状态的组件,以便在输入更改后会显示该消息。

数据绑定

通常,需要将 UI 元素的值绑定到代码中的特定值。 当 UI 元素的值更改时,代码值应更改,当代码值更改时,UI 元素应显示新值。 Blazor 的数据绑定支持可以轻松设置这种双向数据绑定。

可以使用 @bind 属性将 UI 元素绑定到代码中的特定值。 例如:

razor
<input @bind="text" />
<button @onclick="() => text = string.Empty">Clear</button>
<p>@text</p>

@code {
    string text = "";
}

更改输入的值时,text 字段将更新为新值。 单击“清除”按钮更改 text 字段的值时,输入的值也会被清除。

启用交互性

若要处理来自组件的 UI 事件并使用数据绑定,该组件必须是交互式的。 默认情况下,Blazor 组件从服务器静态呈现,这意味着它们生成 HTML 以响应请求,否则无法处理 UI 事件。 可以通过使用 @rendermode 指令应用交互式呈现模式,使组件具有交互性。

可以将 @rendermode 指令应用于组件定义:

razor
@rendermode InteractiveServer

或组件实例:

razor
<Counter @rendermode="InteractiveServer" />

Counter 组件是目前应用中唯一的交互式组件,它使用交互式服务器呈现。 交互式服务器呈现通过与浏览器的 WebSocket 连接处理来自服务器的 UI 事件。 Blazor 通过此连接将 UI 事件发送到服务器,以便应用的组件可以处理它们。 然后,Blazor 会使用呈现的更新来更新浏览器 DOM。

interactive-server

或者,Blazor 组件可以使用 InteractiveWebAssembly 呈现模式以交互方式从客户端呈现。 在此模式下,组件代码将下载到浏览器并使用基于 WebAssembly 的 .NET 运行时运行客户端。

interactive-webassembly

选择使用哪种交互式呈现模式取决于应用的要求。 目前,Blazor 项目仅针对基于服务器的呈现进行设置,因此在本模块中,我们继续使用静态和交互式服务器呈现。

创建待办事项列表

  1. 创建新的 Blazor Web 应用项目。

  2. 将 Todo.razor 文件添加到 Components/Pages 文件夹

在 Visual Studio 和 Visual Studio Code 中,可以通过右键单击解决方案资源管理器中的 Components/Pages 文件夹并选择添加新文件的相应选项来添加 Razor 文件。

还可以使用 .NET CLI 通过以下命令创建 Razor 文件:

sh
$ dotnet new razorcomponent -n Todo -o Components/Pages

上述命令中的 -n|--name 指定了新的 Razor 组件的名称。 新组件是在项目具有 Components/Pages 选项的 -o|--output 文件夹中创建的。

  1. 打开 Todo 组件,生成的模板内容如下:
razor
<h3>Todo</h3>

@code {

}

在文件的顶部添加 @page Razor 指令(其相对 URL 为 /todo),并将呈现模式设置为 InteractiveServer,以便组件可以处理 UI 事件。

razor
@page "/todo"
@rendermode InteractiveServer

<h3>Todo</h3>

@code {

}
  1. 将更改应用到应用程序,并尝试浏览到“/todo”以查看新页面。
将待办事项页面添加到导航菜单

新的待办事项列表页面尚未与其他现有页面一起出现在导航菜单中。 导航菜单在 NavMenu 组件中定义,该组件是应用程序布局的一部分。 更新 NavMenu 组件,以添加指向待办事项列表页的链接。

  1. 打开 Components/Layout/NavMenu.razor

  2. 查找 nav 组件中的 NavMenu 元素,并在天气页的现有导航项下方添加以下 div 元素。

razor
<div class="nav-item px-3">
    <NavLink class="nav-link" href="todo">
        <span class="bi bi-list-nested-nav-menu" aria-hidden="true"></span> Todo
    </NavLink>
</div>

NavLink 组件是一个内置 Blazor 组件,用于呈现定位标记。 如果当前浏览器地址与 hrefNavLink 匹配,它还会呈现一个可用于设置链接样式的 active CSS 类。

应用此更改后,现在应会在导航菜单中显示待办事项页面。

todo-nav

(图片来源:自己截的)

生成待办事项列表
  1. 在项目的根目录中创建新的 TodoItem.cs 文件(与 Program.cs 同级),并向其添加以下 C# 类。
cs
public class TodoItem
{
    public string? Title { get; set; }
    public bool IsDone { get; set; } = false;
}
  1. Todo.razor@code 块中添加 todos 字段。
razor
@code {
    private List<TodoItem> todos = new();
}
  1. 使用 foreach 循环呈现所有 todos 的未排序列表。
razor
<ul>
    @foreach (var todo in todos)
    {
        <li>@todo.Title</li>
    }
</ul>

你还不会看到为待办事项列表呈现的任何内容,因为该列表是空的。 你需要一种方法来添加一些待办事项。

添加待办事项

添加一些 UI 元素,以便向列表添加待办事项。

  1. Todo.razor 中未排序的列表下方添加一个 input 标签和 button 标签。
razor
<input />
<button>Add todo</button>
  1. 创建 newTodo 字符串字段并使用 @bind 指令属性将其添加到 input
razor
<input @bind="newTodo" />
<button>Add todo</button>

@code {
    private List<TodoItem> todos = new();
    string newTodo = string.Empty;
}
  1. @onclick 处理程序添加到 button,该处理程序根据 TodoItem 的值将新的 newTodo 添加到 todos 列表,然后将 newTodo 的值重置为空字符串。
razor
<input @bind="newTodo" />
<button @onclick="AddTodo">Add todo</button>

@code {
    private List<TodoItem> todos = new();
    string newTodo = string.Empty;

    void AddTodo()
    {
        if (!string.IsNullOrWhiteSpace(newTodo))
        {
            todos.Add(new TodoItem { Title = newTodo });
            newTodo = string.Empty;
        }
    }
}
  1. 检查现在是否可以添加待办事项并且它们是否显示在列表中。添加每个待办事项后 input 值也应重置。
添加复选框并计算未完成的待办事项

你需要一种方法将待办事项标记为已完成,编辑现有的待办事项以及计算尚未完成的待办事项数量。

  1. 更新 li 元素的内容以呈现绑定到 inputcheckbox 类型的 todo.IsDone 和绑定到 input 的文本类型的 todo.Title
razor
<ul>
    @foreach (var todo in todos)
    {
        <li>
            <input type="checkbox" @bind="@todo.IsDone" />
            <input @bind="@todo.Title" />
        </li>
    }
</ul>
  1. 更新 <h3> 标头,显示尚未完成的待办项数目(IsDonefalse)。
razor
<h3>Todo (@todos.Count(todo => !todo.IsDone))</h3>
  1. 添加完代码后,Todo.razor 文件应如下所示:
razor
@page "/todo"
@rendermode InteractiveServer

<h3>Todo (@todos.Count(todo => !todo.IsDone))</h3>

<ul>
    @foreach (var todo in todos)
    {
        <li>
            <input type="checkbox" @bind="@todo.IsDone" />
            <input @bind="@todo.Title" />
        </li>
    }
</ul>

<input @bind="newTodo" />
<button @onclick="AddTodo">Add todo</button>

@code {
    private List<TodoItem> todos = new();
    string newTodo = string.Empty;

    void AddTodo()
    {
        if (!string.IsNullOrWhiteSpace(newTodo))
        {
            todos.Add(new TodoItem { Title = newTodo });
            newTodo = string.Empty;
        }
    }
}
  1. 将更改应用到应用程序后,请尝试添加项、编辑项以及标记已完成的待办事项以测试组件。

todo-complete

(图片来源:自己截的)

你的 Blazor 待办事项列表现已完成 ✅。

Last updated:

Released under the MIT License.