ASP.NET Core Blazor 布局
注意
此版本不是本文的最新版本。 对于当前版本,请参阅此文的 .NET 8 版本。
警告
此版本的 ASP.NET Core 不再受支持。 有关详细信息,请参阅 .NET 和 .NET Core 支持策略。 对于当前版本,请参阅此文的 .NET 8 版本。
本文介绍如何为 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 组件集成到 ASP.NET Core 应用中。
当可路由组件集成到 Razor Pages 应用中时,该应用的共享布局可与这些组件配合使用。 有关详细信息,请参阅预呈现和集成 ASP.NET Core Razor 组件。
章节
若要从 Razor 子组件控制布局内容,请参阅 ASP.NET Core Blazor 部分。