生成首个 Blazor 应用Build your first Blazor app
本文内容
作者:Daniel Roth 和 Luke Latham
重要
Blazor WebAssembly 为预览版状态
ASP.NET Core 3.0 支持 Blazor Server。Blazor WebAssembly 在 ASP.NET Core 3.1 中为预览版。
本教程演示如何生成和修改 Blazor 应用。
生成组件Build components
按照 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。
使用组件Use components
使用 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
组件有其自己的计数器。
组件参数Component parameters
组件也可以有参数。组件参数由具有 [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。
路由到组件Route to components
Counter.razor 文件顶部的 @page
指令指定 Counter
组件是路由终结点。Counter
组件处理发送到 /counter
的请求。如果没有 @page
指令,组件将无法处理路由的请求,但该组件仍可以被其他组件使用。
依赖关系注入Dependency injection
Blazor Server 体验 Server experience
如果使用的是 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 体验 WebAssembly experience
如果使用的是 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>
生成待办项列表Build a todo list
向应用添加一个实现简单待办事项列表的新组件。
向“页面”文件夹中的应用添加一个新的
Todo
Razor 组件。在 Visual Studio 中,右键单击“页面”文件夹,然后选择“添加” > “新项目” > “Razor 组件”。将组件的文件命名为 Todo.razor。在其他开发环境中,将空白文件添加到名为 Todo.razor 的“页面”文件夹中。为组件提供初始标记:
@page "/todo"
<h3>Todo</h3>
- 将
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"
<h3>Todo</h3>
<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"
<h3>Todo</h3>
<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"
<h3>Todo</h3>
<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>
- 若要验证这些值是否已绑定,请更新
<h3>
标头以显示尚未完成的待办项计数(IsDone
是false
)。
<h3>Todo (@todos.Count(todo => !todo.IsDone))</h3>
- 完成的
Todo
组件 (Pages/Todo.razor):
@page "/todo"
<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 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;
}
}
}
- 重新生成并运行应用。添加待办项以测试新代码。