ASP.NET Core Blazor 布局
注意
此版本不是本文的最新版本。 有关当前版本,请参阅本文的 .NET 9 版本。
警告
此版本的 ASP.NET Core 不再受支持。 有关详细信息,请参阅 .NET 和 .NET Core 支持策略。 有关当前版本,请参阅本文的 .NET 9 版本。
本文介绍如何为 Blazor 应用创建可重用布局组件。
Blazor 布局的有用性
有些应用元素(例如菜单、版权消息和公司徽标)通常是应用整体布局的一部分。 将这些元素的标记副本放入应用的所有组件是一种效率较低的做法。 每次更新其中一个元素时,都必须同时更新使用该元素的每个组件。 这种方法的维护成本很高,并且如果缺少更新,还可能会导致内容不一致。 “布局”可以解决这些问题。
Blazor 布局是一个 Razor 组件,它与引用它的组件共享标记。 布局可以使用数据绑定、依赖关系注入和组件的其他功能。
布局组件
创建布局组件
要创建布局组件:
- 创建由 Razor 模板或 C# 代码定义的 Razor 组件。 基于 Razor 模板的布局组件像普通 Razor 组件一样使用
.razor
文件扩展名。 由于布局组件是在应用组件间共享的,因此它们通常放置在应用的Shared
或Layout
文件夹中。 但是,布局可以放置在使用它的组件可访问的任何位置。 例如,可以将布局放在使用它的组件所在的同一文件夹中。 - 组件继承自 LayoutComponentBase。 LayoutComponentBase 为布局内呈现的内容定义 Body 属性(RenderFragment 类型)。
- 使用 Razor 语法
@Body
在布局标记中指定呈现内容的位置。
注意
有关 RenderFragment 的详细信息,请参阅 ASP.NET Core Razor 组件。
以下 DoctorWhoLayout
组件显示布局组件的 Razor 模板。 布局继承 LayoutComponentBase 并在导航栏 (<nav>...</nav>
) 和页脚 (<footer>...</footer>
) 之间设置 @Body
。
DoctorWhoLayout.razor
:
@inherits LayoutComponentBase
<PageTitle>Doctor Who® Database</PageTitle>
<header>
<h1>Doctor Who® Database</h1>
</header>
<nav>
<a href="main-list">Main Episode List</a>
<a href="search">Search</a>
<a href="new">Add Episode</a>
</nav>
@Body
<footer>
@TrademarkMessage
</footer>
@code {
public string TrademarkMessage { get; set; } =
"Doctor Who is a registered trademark of the BBC. " +
"https://www.doctorwho.tv/ https://www.bbc.com";
}
@inherits LayoutComponentBase
<PageTitle>Doctor Who® Database</PageTitle>
<header>
<h1>Doctor Who® Database</h1>
</header>
<nav>
<a href="main-list">Main Episode List</a>
<a href="search">Search</a>
<a href="new">Add Episode</a>
</nav>
@Body
<footer>
@TrademarkMessage
</footer>
@code {
public string TrademarkMessage { get; set; } =
"Doctor Who is a registered trademark of the BBC. " +
"https://www.doctorwho.tv/ https://www.bbc.com";
}
@inherits LayoutComponentBase
<header>
<h1>Doctor Who™ Episode Database</h1>
</header>
<nav>
<a href="main-list">Main Episode List</a>
<a href="search">Search</a>
<a href="new">Add Episode</a>
</nav>
@Body
<footer>
@TrademarkMessage
</footer>
@code {
public string TrademarkMessage { get; set; } =
"Doctor Who is a registered trademark of the BBC. " +
"https://www.doctorwho.tv/";
}
@inherits LayoutComponentBase
<header>
<h1>Doctor Who™ Episode Database</h1>
</header>
<nav>
<a href="main-list">Main Episode List</a>
<a href="search">Search</a>
<a href="new">Add Episode</a>
</nav>
@Body
<footer>
@TrademarkMessage
</footer>
@code {
public string TrademarkMessage { get; set; } =
"Doctor Who is a registered trademark of the BBC. " +
"https://www.doctorwho.tv/";
}
@inherits LayoutComponentBase
<header>
<h1>Doctor Who™ Episode Database</h1>
</header>
<nav>
<a href="main-list">Main Episode List</a>
<a href="search">Search</a>
<a href="new">Add Episode</a>
</nav>
@Body
<footer>
@TrademarkMessage
</footer>
@code {
public string TrademarkMessage { get; set; } =
"Doctor Who is a registered trademark of the BBC. " +
"https://www.doctorwho.tv/";
}
@inherits LayoutComponentBase
<header>
<h1>Doctor Who™ Episode Database</h1>
</header>
<nav>
<a href="main-list">Main Episode List</a>
<a href="search">Search</a>
<a href="new">Add Episode</a>
</nav>
@Body
<footer>
@TrademarkMessage
</footer>
@code {
public string TrademarkMessage { get; set; } =
"Doctor Who is a registered trademark of the BBC. " +
"https://www.doctorwho.tv/";
}
MainLayout
组件
在从 Blazor 项目模板创建的应用中,MainLayout
组件就是应用的默认布局。 Blazor 的布局采用 Flexbox layout model (MDN documentation)(W3C 规范)。
Blazor 的 CSS 隔离功能将独立 CSS 样式应用于 MainLayout
组件。 按照惯例,样式由相同名称的随附样式表 MainLayout.razor.css
提供。 ASP.NET Core 框架的样式表实现可以在 ASP.NET Core 参考源(dotnet/aspnetcore
GitHub 存储库)中查阅:
注意
指向 .NET 参考源的文档链接通常会加载存储库的默认分支,该分支表示针对下一个 .NET 版本的当前开发。 若要为特定版本选择标记,请使用“切换分支或标记”下拉列表。 有关详细信息,请参阅如何选择 ASP.NET Core 源代码的版本标记 (dotnet/AspNetCore.Docs #26205)。
Blazor 的 CSS 隔离功能将独立 CSS 样式应用于 MainLayout
组件。 按照惯例,样式由相同名称的随附样式表 MainLayout.razor.css
提供。
应用布局
使布局命名空间可用
Blazor 框架的布局文件位置和命名空间随时间而变。 根据 Blazor 的版本和你要构建的 Blazor 应用的类型,你可能需要在使用它时指明布局的命名空间。 在引用布局实现时,如果没有指明布局的命名空间就找不到布局,请采用以下任一方法:
向
_Imports.razor
文件添加@using
指令,用于指定布局的位置。 在以下示例中,名称为Layout
的布局文件夹位于Components
文件夹中,应用的命名空间为BlazorSample
:@using BlazorSample.Components.Layout
在使用此布局的组件定义顶部添加
@using
指令:@using BlazorSample.Components.Layout @layout DoctorWhoLayout
完全限定使用布局的命名空间:
@layout BlazorSample.Components.Layout.DoctorWhoLayout
向组件应用布局
使用 @layout
Razor 指令将布局应用于具有 @page
指令的可路由 Razor 组件。 编译器将 @layout
转换为 LayoutAttribute,并将特性应用于组件类。
以下 Episodes
组件的内容插入到 DoctorWhoLayout
中的 @Body
位置:
Episodes.razor
:
@page "/episodes"
@layout DoctorWhoLayout
<h2>Doctor Who® Episodes</h2>
<ul>
<li>
<a href="https://www.bbc.co.uk/programmes/p00vfknq">
<em>The Ribos Operation</em>
</a>
</li>
<li>
<a href="https://www.bbc.co.uk/programmes/p00vfdsb">
<em>The Sunmakers</em>
</a>
</li>
<li>
<a href="https://www.bbc.co.uk/programmes/p00vhc26">
<em>Nightmare of Eden</em>
</a>
</li>
</ul>
@page "/episodes"
@layout DoctorWhoLayout
<h2>Doctor Who® Episodes</h2>
<ul>
<li>
<a href="https://www.bbc.co.uk/programmes/p00vfknq">
<em>The Ribos Operation</em>
</a>
</li>
<li>
<a href="https://www.bbc.co.uk/programmes/p00vfdsb">
<em>The Sunmakers</em>
</a>
</li>
<li>
<a href="https://www.bbc.co.uk/programmes/p00vhc26">
<em>Nightmare of Eden</em>
</a>
</li>
</ul>
@page "/episodes"
@layout DoctorWhoLayout
<h2>Episodes</h2>
<ul>
<li>
<a href="https://www.bbc.co.uk/programmes/p00vfknq">
<em>The Ribos Operation</em>
</a>
</li>
<li>
<a href="https://www.bbc.co.uk/programmes/p00vfdsb">
<em>The Sun Makers</em>
</a>
</li>
<li>
<a href="https://www.bbc.co.uk/programmes/p00vhc26">
<em>Nightmare of Eden</em>
</a>
</li>
</ul>
@page "/episodes"
@layout DoctorWhoLayout
<h2>Episodes</h2>
<ul>
<li>
<a href="https://www.bbc.co.uk/programmes/p00vfknq">
<em>The Ribos Operation</em>
</a>
</li>
<li>
<a href="https://www.bbc.co.uk/programmes/p00vfdsb">
<em>The Sun Makers</em>
</a>
</li>
<li>
<a href="https://www.bbc.co.uk/programmes/p00vhc26">
<em>Nightmare of Eden</em>
</a>
</li>
</ul>
@page "/episodes"
@layout DoctorWhoLayout
<h2>Episodes</h2>
<ul>
<li>
<a href="https://www.bbc.co.uk/programmes/p00vfknq">
<em>The Ribos Operation</em>
</a>
</li>
<li>
<a href="https://www.bbc.co.uk/programmes/p00vfdsb">
<em>The Sun Makers</em>
</a>
</li>
<li>
<a href="https://www.bbc.co.uk/programmes/p00vhc26">
<em>Nightmare of Eden</em>
</a>
</li>
</ul>
以下呈现的 HTML 标记由前面的 DoctorWhoLayout
和 Episodes
组件生成。 此处不会出现无关标记,以使读者能够专注于这两个相关组件提供的内容:
- 标头(
<header>...</header>
)中的 H1“数据库”标题(<h1>...</h1>
)、导航栏(<nav>...</nav>
),以及页脚(<footer>...</footer>
)中的商标信息来自DoctorWhoLayout
组件。 - H2“剧集”标题(
<h2>...</h2>
)和剧集列表(<ul>...</ul>
)来自Episodes
组件。
<header>
<h1 ...>...</h1>
</header>
<nav>
...
</nav>
<h2>...</h2>
<ul>
<li>...</li>
<li>...</li>
<li>...</li>
</ul>
<footer>
...
</footer>
直接在组件中指定布局会覆盖默认布局:
- 由从
_Imports.razor
文件导入的@layout
指令设置,如下文向组件文件夹应用布局部分所述。 - 设置为应用的默认布局,详见本文后面的将默认布局应用于应用部分所述。
向组件文件夹应用布局
应用的每个文件夹都可以选择包含名为 _Imports.razor
的模板文件。 编译器将导入文件中指定的指令包括在同一文件夹中的所有 Razor 模板内,并在其所有子文件夹中以递归方式包括。 因此,包含 @layout DoctorWhoLayout
的 _Imports.razor
文件可确保文件夹中的所有组件都使用 DoctorWhoLayout
组件。 无需将 @layout DoctorWhoLayout
重复添加到文件夹和子文件夹内的所有 Razor 组件 (.razor
)。
_Imports.razor
:
@layout DoctorWhoLayout
...
_Imports.razor
文件类似于 Razor 视图和页面的 _ViewImports.cshtml 文件,但专门应用于 Razor 组件文件。
在 _Imports.razor
中指定布局会覆盖指定为路由器的默认应用布局的布局,如下一部分所述。
警告
请勿向根 _Imports.razor
文件添加 Razor@layout
指令,这会导致布局形成无限循环。 请在 Router 组件中指定布局,以控制默认应用布局。 有关更多信息,请参阅下文将默认布局应用于应用部分。
将默认布局应用于应用
在 Router 组件的 RouteView 组件中指定默认应用布局。 使用 DefaultLayout 参数设置布局类型:
<RouteView RouteData="routeData" DefaultLayout="typeof({LAYOUT})" />
在前面的示例中,{LAYOUT}
占位符是布局(例如,如果布局文件名为 DoctorWhoLayout.razor
,则它为 DoctorWhoLayout
)。 可能需要根据 .NET 版本和 Blazor 应用的类型来确定布局的命名空间。 有关详细信息,请参阅使布局命名空间可用部分。
在 Router 组件的 RouteView 中将布局指定为默认布局是一种实用的做法,因为你可以为每个组件或每个文件夹替代布局,如本文前面几个部分所述。 建议使用 Router 组件设置应用的默认布局,因为它是使用布局的最通用且灵活的方法。
将布局应用于任意内容(LayoutView
组件)
若要为任意 Razor 模板内容设置布局,请使用 LayoutView 组件指定布局。 您可以在任何 Razor 组件中使用 LayoutView。 下面的示例为 MainLayout
组件的 NotFound 模板 (<NotFound>...</NotFound>
) 设置了一个名为 ErrorLayout
的布局组件。
<Router ...>
<Found ...>
...
</Found>
<NotFound>
<LayoutView Layout="typeof(ErrorLayout)">
<h1>Page not found</h1>
<p>Sorry, there's nothing at this address.</p>
</LayoutView>
</NotFound>
</Router>
可能需要根据 .NET 版本和 Blazor 应用的类型来 identity 布局的命名空间。 有关详细信息,请参阅使布局命名空间可用部分。
重要
Blazor Web App 不使用 NotFound 参数(<NotFound>...</NotFound>
标记),但该参数受到支持以实现向后兼容,从而避免在框架中发生中断性变更。 服务器端 ASP.NET 核心中间件管道处理服务器上的请求。 使用服务器端技术来处理错误的请求。 有关详细信息,请参阅 ASP.NET Core Blazor 呈现模式。
注意
随着 ASP.NET Core 5.0.1 的发布及任何附加 5.x 版本的推出,Router
组件包含 PreferExactMatches
参数(设置为 @true
)。 有关详细信息,请参阅从 ASP.NET Core 3.1 迁移到 5.0。
嵌套布局
组件可以引用一个布局,该布局又可以引用另一个布局。 例如,嵌套布局可用于创建多级菜单结构。
以下示例演示如何使用嵌套布局。 向组件应用布局部分中显示的 Episodes
组件是要显示的组件。 该组件引用 DoctorWhoLayout
组件。
以下 DoctorWhoLayout
组件是前文所示示例的修改版本。 标头和页脚元素已经删除,并且布局引用了另一个布局 ProductionsLayout
。 在 DoctorWhoLayout
中出现 @Body
的位置,呈现 Episodes
组件。
DoctorWhoLayout.razor
:
@inherits LayoutComponentBase
@layout ProductionsLayout
<PageTitle>Doctor Who® Database</PageTitle>
<h1>Doctor Who® Database</h1>
<nav>
<a href="main-episode-list">Main Episode List</a>
<a href="episode-search">Search</a>
<a href="new-episode">Add Episode</a>
</nav>
@Body
<div>
@TrademarkMessage
</div>
@code {
public string TrademarkMessage { get; set; } =
"Doctor Who is a registered trademark of the BBC. " +
"https://www.doctorwho.tv/ https://www.bbc.com";
}
@inherits LayoutComponentBase
@layout ProductionsLayout
<PageTitle>Doctor Who® Database</PageTitle>
<h1>Doctor Who® Database</h1>
<nav>
<a href="main-episode-list">Main Episode List</a>
<a href="episode-search">Search</a>
<a href="new-episode">Add Episode</a>
</nav>
@Body
<div>
@TrademarkMessage
</div>
@code {
public string TrademarkMessage { get; set; } =
"Doctor Who is a registered trademark of the BBC. " +
"https://www.doctorwho.tv/ https://www.bbc.com";
}
@inherits LayoutComponentBase
@layout ProductionsLayout
<h1>Doctor Who™ Episode Database</h1>
<nav>
<a href="main-episode-list">Main Episode List</a>
<a href="episode-search">Search</a>
<a href="new-episode">Add Episode</a>
</nav>
@Body
<div>
@TrademarkMessage
</div>
@code {
public string TrademarkMessage { get; set; } =
"Doctor Who is a registered trademark of the BBC. " +
"https://www.doctorwho.tv/";
}
@inherits LayoutComponentBase
@layout ProductionsLayout
<h1>Doctor Who™ Episode Database</h1>
<nav>
<a href="main-episode-list">Main Episode List</a>
<a href="episode-search">Search</a>
<a href="new-episode">Add Episode</a>
</nav>
@Body
<div>
@TrademarkMessage
</div>
@code {
public string TrademarkMessage { get; set; } =
"Doctor Who is a registered trademark of the BBC. " +
"https://www.doctorwho.tv/";
}
@inherits LayoutComponentBase
@layout ProductionsLayout
<h1>Doctor Who™ Episode Database</h1>
<nav>
<a href="main-episode-list">Main Episode List</a>
<a href="episode-search">Search</a>
<a href="new-episode">Add Episode</a>
</nav>
@Body
<div>
@TrademarkMessage
</div>
@code {
public string TrademarkMessage { get; set; } =
"Doctor Who is a registered trademark of the BBC. " +
"https://www.doctorwho.tv/";
}
@inherits LayoutComponentBase
@layout ProductionsLayout
<h1>Doctor Who™ Episode Database</h1>
<nav>
<a href="main-episode-list">Main Episode List</a>
<a href="episode-search">Search</a>
<a href="new-episode">Add Episode</a>
</nav>
@Body
<div>
@TrademarkMessage
</div>
@code {
public string TrademarkMessage { get; set; } =
"Doctor Who is a registered trademark of the BBC. " +
"https://www.doctorwho.tv/";
}
ProductionsLayout
组件包含顶级布局元素,其中包含有标头 (<header>...</header>
) 和页脚 (<footer>...</footer>
) 元素。 具有 Episodes
组件的 DoctorWhoLayout
会在出现 @Body
的位置呈现。
ProductionsLayout.razor
:
@inherits LayoutComponentBase
<header>
<h1>Productions</h1>
</header>
<nav>
<a href="main-production-list">Main Production List</a>
<a href="production-search">Search</a>
<a href="new-production">Add Production</a>
</nav>
@Body
<footer>
Footer of Productions Layout
</footer>
@inherits LayoutComponentBase
<header>
<h1>Productions</h1>
</header>
<nav>
<a href="main-production-list">Main Production List</a>
<a href="production-search">Search</a>
<a href="new-production">Add Production</a>
</nav>
@Body
<footer>
Footer of Productions Layout
</footer>
@inherits LayoutComponentBase
<header>
<h1>Productions</h1>
</header>
<nav>
<a href="main-production-list">Main Production List</a>
<a href="production-search">Search</a>
<a href="new-production">Add Production</a>
</nav>
@Body
<footer>
Footer of Productions Layout
</footer>
@inherits LayoutComponentBase
<header>
<h1>Productions</h1>
</header>
<nav>
<a href="main-production-list">Main Production List</a>
<a href="production-search">Search</a>
<a href="new-production">Add Production</a>
</nav>
@Body
<footer>
Footer of Productions Layout
</footer>
@inherits LayoutComponentBase
<header>
<h1>Productions</h1>
</header>
<nav>
<a href="main-production-list">Main Production List</a>
<a href="production-search">Search</a>
<a href="new-production">Add Production</a>
</nav>
@Body
<footer>
Footer of Productions Layout
</footer>
@inherits LayoutComponentBase
<header>
<h1>Productions</h1>
</header>
<nav>
<a href="main-production-list">Main Production List</a>
<a href="production-search">Search</a>
<a href="new-production">Add Production</a>
</nav>
@Body
<footer>
Footer of Productions Layout
</footer>
以下呈现的 HTML 标记由前面的嵌套布局生成。 此处不会出现无关标记,以使读者能够专注于这三个相关组件提供的嵌套内容:
- 标头 (
<header>...</header>
)、生产导航栏 (<nav>...</nav>
) 和页脚 (<footer>...</footer>
) 元素以及它们的内容来自于ProductionsLayout
组件。 - H1“数据库”标题(
<h1>...</h1>
)、剧集导航栏(<nav>...</nav>
)和商标信息(<div>...</div>
)来自DoctorWhoLayout
组件。 - H2“剧集”标题(
<h2>...</h2>
)和剧集列表(<ul>...</ul>
)来自Episodes
组件。
<header>
...
</header>
<nav>
<a href="main-production-list">Main Production List</a>
<a href="production-search">Search</a>
<a href="new-production">Add Production</a>
</nav>
<h1>...</h1>
<nav>
<a href="main-episode-list">Main Episode List</a>
<a href="episode-search">Search</a>
<a href="new-episode">Add Episode</a>
</nav>
<h2>...</h2>
<ul>
<li>...</li>
<li>...</li>
<li>...</li>
</ul>
<div>
...
</div>
<footer>
...
</footer>
与集成组件共享 Razor Pages 布局
当可路由组件集成到 Razor Pages 应用中时,该应用的共享布局可与这些组件配合使用。 有关详细信息,请参阅 将 ASP.NET Core Razor 组件与 MVC 或 Razor Pages 集成。
章节
若要从 Razor 子组件控制布局内容,请参阅 ASP.NET Core Blazor 部分。