生成首个 Blazor 应用
作者:Daniel Roth 和 Luke Latham
重要
Blazor WebAssembly 为预览版状态
ASP.NET Core 3.0 支持 Blazor Server 。 Blazor WebAssembly 在 ASP.NET Core 3.1 中为预览版。
本教程演示如何生成和修改 Blazor 应用。
按照 ASP.NET Core Blazor 入门 文章中的指南创建用于本教程的 Blazor 项目。 将项目命名为 ToDoList 。
生成组件
在 Pages 文件夹中浏览应用的三个页面:主页、计数器和提取数据。 这些页面由 Razor 组件文件(Index.razor 、Counter.razor 和 FetchData.razor )实现。
在“计数器”页上,选择“单击我” 按钮,在不刷新页面的情况下增加计数器值。 增加网页的计数器值通常需要编写 JavaScript。 通过 Blazor,可以改为编写 C#。
检查 Counter.razor 文件中
Counter
组件的实现。Pages/Counter.razor:
@page "/counter" <h1>Counter</h1> <p>Current count: @currentCount</p> <button class="btn btn-primary" @onclick="IncrementCount">Click me</button> @code { private int currentCount = 0; private void IncrementCount() { currentCount++; } }
使用 HTML 定义
Counter
组件的 UI。 动态呈现逻辑(例如,循环、条件、表达式)是使用名为 Razor 的嵌入式 C# 语法添加的。 HTML 标记和 C# 呈现逻辑在构建时转换为组件类。 生成的 .NET 类的名称与文件名匹配。组件类的成员在
@code
块中定义。 在@code
块中,可以指定组件状态(属性、字段)和方法用于处理事件或定义其他组件逻辑。 然后,可以将这些成员用作组件呈现逻辑的一部分,并用于处理事件。选中“单击我” 按钮时:
- 调用
Counter
组件的已注册onclick
处理程序(IncrementCount
方法)。 Counter
组件重新生成其呈现树。- 将新的呈现树与前一个呈现树进行比较。
- 仅应用对文档对象模型 (DOM) 的修改。 显示的计数将会更新。
- 调用
修改
Counter
组件的 C# 逻辑,使计数递增 2 而不是 1。@page "/counter" <h1>Counter</h1> <p>Current count: @currentCount</p> <button class="btn btn-primary" @onclick="IncrementCount">Click me</button> @code { private int currentCount = 0; private void IncrementCount() { currentCount += 2; } }
重新生成并运行应用以查看更改。 选择“单击我” 按钮。 计数器的值将增加 2。
使用组件
使用 HTML 语法将组件加入到另一个组件中。
通过向
Index
组件 (Index.razor ) 添加<Counter />
元素,将Counter
组件添加到应用的Index
组件。如果在此体验中使用的是 Blazor WebAssembly,则
Index
组件使用SurveyPrompt
组件。 将<SurveyPrompt>
元素替换为<Counter />
元素。 如果在此体验中使用的是 Blazor Server 应用,请向Index
组件添加<Counter />
元素:Pages/Index.razor:
@page "/" <h1>Hello, world!</h1> Welcome to your new app. <Counter />
重新生成并运行应用。
Index
组件有其自己的计数器。
组件参数
组件也可以有参数。 组件参数由具有 [Parameter]
的组件类上的公共属性定义。 使用这些属性在标记中为组件指定参数。
更新组件的
@code
C# 代码,如下所示:- 使用
[Parameter]
特性添加公共IncrementAmount
属性。 - 增加
currentCount
的值时,更改IncrementCount
方法以使用IncrementAmount
属性。
Pages/Counter.razor:
@page "/counter" <h1>Counter</h1> <p>Current count: @currentCount</p> <button class="btn btn-primary" @onclick="IncrementCount">Click me</button> @code { private int currentCount = 0; [Parameter] public int IncrementAmount { get; set; } = 1; private void IncrementCount() { currentCount += IncrementAmount; } }
- 使用
使用属性在
Index
组件的<Counter>
元素中指定IncrementAmount
参数。 将计数器递增值设置为 10。Pages/Index.razor:
@page "/" <h1>Hello, world!</h1> Welcome to your new app. <Counter IncrementAmount="10" />
重新加载
Index
组件。 每次选择“单击我” 按钮时,计数器值递增 10。Counter
组件中的计数器继续递增 1。
路由到组件
Counter.razor 文件顶部的 @page
指令指定 Counter
组件是路由终结点。 Counter
组件处理发送到 /counter
的请求。 如果没有 @page
指令,组件将无法处理路由的请求,但该组件仍可以被其他组件使用。
依赖关系注入
Blazor Server 体验
如果使用的是 Blazor Server 应用,则 WeatherForecastService
服务在 Startup.ConfigureServices
中注册为单一实例。 可通过依赖关系注入 (DI) 在整个应用中使用服务的实例:
public void ConfigureServices(IServiceCollection services) { services.AddRazorPages(); services.AddServerSideBlazor(); services.AddSingleton<WeatherForecastService>(); }
@inject
指令用于将 WeatherForecastService
服务的实例注入到 FetchData
组件中。
Pages/FetchData.razor:
@page "/fetchdata" @using ToDoList.Data @inject WeatherForecastService ForecastService
FetchData
组件使用注入的服务(作为 ForecastService
)来检索 WeatherForecast
对象的数组:
@code { private WeatherForecast[] forecasts; protected override async Task OnInitializedAsync() { forecasts = await ForecastService.GetForecastAsync(DateTime.Now); } }
Blazor WebAssembly 体验
如果使用的是 Blazor WebAssembly 应用,则注入了 HttpClient
,以从 wwwroot/sample-data 文件夹的 weather.json 文件中获取天气预测数据。
Pages/FetchData.razor:
@inject HttpClient Http ... protected override async Task OnInitializedAsync() { forecasts = await Http.GetJsonAsync<WeatherForecast[]>("sample-data/weather.json"); }
@foreach
循环用于将每个预测实例呈现为“天气”数据表中的一行:
<table class="table"> <thead> <tr> <th>Date</th> <th>Temp. (C)</th> <th>Temp. (F)</th> <th>Summary</th> </tr> </thead> <tbody> @foreach (var forecast in forecasts) { <tr> <td>@forecast.Date.ToShortDateString()</td> <td>@forecast.TemperatureC</td> <td>@forecast.TemperatureF</td> <td>@forecast.Summary</td> </tr> } </tbody> </table>
生成待办项列表
向应用添加一个实现简单待办事项列表的新组件。
向 Pages 文件夹中的应用添加一个名为 Todo.razor 的空文件:
为组件提供初始标记:
@page "/todo" <h1>Todo</h1>
将
Todo
组件添加到导航栏。NavMenu
组件 (Shared/NavMenu.razor ) 用于应用的布局。 布局是可以避免应用中出现重复内容的组件。通过在“Shared/NavMenu.razor” 文件中的现有列表项下添加以下列表项标记,为
Todo
组件添加一个<NavLink>
元素:<li class="nav-item px-3"> <NavLink class="nav-link" href="todo"> <span class="oi oi-list-rich" aria-hidden="true"></span> Todo </NavLink> </li>
重新生成并运行应用。 访问新的“待办事项”页面,确认指向
Todo
组件的链接有效。向项目的根目录添加“TodoItem.cs” 文件,以保存一个用于表示待办项的类。 为
TodoItem
类使用以下 C# 代码:public class TodoItem { public string Title { get; set; } public bool IsDone { get; set; } }
返回到
Todo
组件 (Pages/Todo.razor ):- 在
@code
块中为待办项添加一个字段。Todo
组件使用此字段来维护待办项列表的状态。 - 添加无序列表标记和
foreach
循环,以将每个待办项呈现为列表项 (<li>
)。
@page "/todo" <h1>Todo</h1> <ul> @foreach (var todo in todos) { <li>@todo.Title</li> } </ul> @code { private IList<TodoItem> todos = new List<TodoItem>(); }
- 在
该应用需要 UI 元素来将待办项添加到列表。 在未排序列表 (
<ul>...</ul>
) 下方添加一个文本输入 (<input>
) 和一个按钮 (<button>
):@page "/todo" <h1>Todo</h1> <ul> @foreach (var todo in todos) { <li>@todo.Title</li> } </ul> <input placeholder="Something todo" /> <button>Add todo</button> @code { private IList<TodoItem> todos = new List<TodoItem>(); }
重新生成并运行应用。 选择“添加待办项” 按钮时没有任何反应,因为没有事件处理程序连接到该按钮。
向
Todo
组件添加AddTodo
方法,并使用@onclick
属性注册该方法以选择按钮。 选择按钮时,会调用AddTodo
C# 方法:<input placeholder="Something todo" /> <button @onclick="AddTodo">Add todo</button> @code { private IList<TodoItem> todos = new List<TodoItem>(); private void AddTodo() { // Todo: Add the todo } }
要获得新待办项标题,请在
@code
块顶部添加newTodo
字符串字段,并使用<input>
元素中的bind
属性将其绑定到文本输入的值:private IList<TodoItem> todos = new List<TodoItem>(); private string newTodo;
<input placeholder="Something todo" @bind="newTodo" />
更新
AddTodo
方法,将具有指定标题的TodoItem
添加到列表。 通过将newTodo
设置为空字符串来清除文本输入的值:@page "/todo" <h1>Todo</h1> <ul> @foreach (var todo in todos) { <li>@todo.Title</li> } </ul> <input placeholder="Something todo" @bind="newTodo" /> <button @onclick="AddTodo">Add todo</button> @code { private IList<TodoItem> todos = new List<TodoItem>(); private string newTodo; private void AddTodo() { if (!string.IsNullOrWhiteSpace(newTodo)) { todos.Add(new TodoItem { Title = newTodo }); newTodo = string.Empty; } } }
重新生成并运行应用。 在待办项列表中添加一些待办项以测试新代码。
每个待办项的标题文本都可以编辑,复选框可以帮助用户跟踪已完成的项。 为每个待办项添加一个复选框输入,并将它的值绑定到
IsDone
属性。 将@todo.Title
更改为绑定到@todo.Title
的<input>
元素:<ul> @foreach (var todo in todos) { <li> <input type="checkbox" @bind="todo.IsDone" /> <input @bind="todo.Title" /> </li> } </ul>
若要验证这些值是否已绑定,请更新
<h1>
标头以显示尚未完成的待办项计数(IsDone
是false
)。<h1>Todo (@todos.Count(todo => !todo.IsDone))</h1>
完成的
Todo
组件 (Pages/Todo.razor ):@page "/todo" <h1>Todo (@todos.Count(todo => !todo.IsDone))</h1> <ul> @foreach (var todo in todos) { <li> <input type="checkbox" @bind="todo.IsDone" /> <input @bind="todo.Title" /> </li> } </ul> <input placeholder="Something todo" @bind="newTodo" /> <button @onclick="AddTodo">Add todo</button> @code { private IList<TodoItem> todos = new List<TodoItem>(); private string newTodo; private void AddTodo() { if (!string.IsNullOrWhiteSpace(newTodo)) { todos.Add(new TodoItem { Title = newTodo }); newTodo = string.Empty; } } }
重新生成并运行应用。 添加待办项以测试新代码。