SxAppShell (应用外壳)
- Implemented
提供响应式应用基础布局,包含侧边栏、顶部导航栏和主内容区域。支持自动响应式切换、内部导航、多 Tab 历史栈等高级功能。
使用场景
- 应用级响应式布局(桌面/移动端自适应)
- 侧边栏 + 顶部导航栏布局
- 单页应用(SPA)内部导航
- 多 Tab 页面缓存与历史管理
基本用法
最简单的用法
<SxAppRoot>
<SxAppShell TitleText="我的应用">
<Navigation>
<SxNavMenu>
<SxNavMenuLink Text="首页" IconName="house" Href="/" />
<SxNavMenuLink Text="设置" IconName="gear" Href="/settings" />
</SxNavMenu>
</Navigation>
<Content>
@(context => @<div>主内容区域</div>)
</Content>
</SxAppShell>
</SxAppRoot>
完整示例
<SxAppRoot>
<SxAppShell TitleText="企业管理系统"
ShowCollapseToggle="true"
@bind-Collapsed="_collapsed"
SizingMode="SxAppShellSizingMode.Viewport"
BreakpointChanged="OnBreakpointChanged">
<Navigation>
@RenderNavigation
</Navigation>
<Content>
@(isWide => @<div class="@(isWide ? "wide-layout" : "narrow-layout")">
@Body
</div>)
</Content>
</SxAppShell>
</SxAppRoot>
@code {
private bool _collapsed;
private RenderFragment RenderNavigation => __builder =>
{
<SxNavMenu>
<SxNavMenuGroup Text="用户管理" IconName="users">
<SxNavMenuLink Text="用户列表" Href="/users" />
<SxNavMenuLink Text="角色管理" Href="/roles" />
</SxNavMenuGroup>
<SxNavMenuLink Text="系统设置" IconName="gear" Href="/settings" />
</SxNavMenu>
};
private void OnBreakpointChanged(SxAppShell.Breakpoint breakpoint)
{
Console.WriteLine($"断点变化: {breakpoint}");
}
}
布局模式
SxAppShell 支持三种布局模式,通过 LayoutMode 参数控制:
自动模式(默认)
<SxAppShell LayoutMode="null">
<!-- 宽屏(LG/XL)使用侧边栏,窄屏(XS/SM/MD)使用抽屉 -->
</SxAppShell>
- 宽屏(≥LG):固定侧边栏 + 可拖拽分割条
- 窄屏(<LG):抽屉式导航 + 汉堡菜单按钮
强制侧边栏模式
<SxAppShell LayoutMode="SxAppShellLayoutMode.Sidebar">
<!-- 始终使用侧边栏布局,即使在移动端 -->
</SxAppShell>
强制抽屉模式
<SxAppShell LayoutMode="SxAppShellLayoutMode.Drawer">
<!-- 始终使用抽屉布局,侧边显示窄条(Rail) -->
</SxAppShell>
响应式断点
默认断点
| 断点 | 宽度 | 布局行为 |
|---|---|---|
| XS | < 576px | 抽屉模式,侧边栏完全隐藏 |
| SM | ≥ 576px | 抽屉模式,侧边栏完全隐藏 |
| MD | ≥ 768px | 抽屉模式,显示窄条(Rail) |
| LG | ≥ 992px | 侧边栏模式 |
| XL | ≥ 1200px | 侧边栏模式 |
自定义断点
<SxAppShell BreakpointConfig="@_breakpointConfig">
...
</SxAppShell>
@code {
private SxAppShellBreakpointConfig _breakpointConfig = new()
{
XS = "0px",
SM = "480px",
MD = "768px",
LG = "1024px",
XL = "1280px"
};
}
容器尺寸模式
默认情况下,断点基于视口(Viewport)宽度计算。如果 SxAppShell 嵌入在容器中,可以使用容器尺寸:
<div style="width: 800px; height: 600px;">
<SxAppShell SizingMode="SxAppShellSizingMode.Container">
<!-- 断点基于容器宽度而非视口宽度 -->
</SxAppShell>
</div>
侧边栏折叠
基本折叠控制
<SxAppShell @bind-Collapsed="_collapsed"
ShowCollapseToggle="true">
...
</SxAppShell>
@code {
private bool _collapsed;
private void ExpandSidebar() => _collapsed = false;
private void CollapseSidebar() => _collapsed = true;
}
折叠宽度
<SxAppShell Collapsed="true"
CollapsedWidth="60px">
<!-- 折叠时侧边栏宽度为 60px -->
</SxAppShell>
窄条(Rail)显示控制
<!-- 自动:MD 及以上显示窄条,SM/XS 隐藏 -->
<SxAppShell CollapsedRailMode="SxAppShellCollapsedRailMode.Auto" />
<!-- 始终显示窄条 -->
<SxAppShell CollapsedRailMode="SxAppShellCollapsedRailMode.Show" />
<!-- 始终隐藏窄条 -->
<SxAppShell CollapsedRailMode="SxAppShellCollapsedRailMode.Hidden" />
始终显示侧边栏
在小屏幕上也保持显示折叠的侧边栏(窄条):
<SxAppShell AlwaysShowSidebar="true">
<!-- 即使在 XS/SM 断点也显示窄条 -->
</SxAppShell>
折叠时显示内容区导航栏
<!-- 默认行为:折叠时在内容区顶部显示导航栏 -->
<SxAppShell Collapsed="true" ShowContentNavBarWhenCollapsed="true" />
<!-- 禁用:折叠时不显示内容区导航栏 -->
<SxAppShell Collapsed="true" ShowContentNavBarWhenCollapsed="false" />
抽屉遮罩
控制抽屉展开时的遮罩层行为:
<!-- 透明遮罩,点击可关闭抽屉 -->
<SxAppShell DrawerOverlayMode="SxAppShellDrawerOverlayMode.Transparent" />
<!-- 隐藏遮罩,抽屉不遮挡内容区 -->
<SxAppShell DrawerOverlayMode="SxAppShellDrawerOverlayMode.Hidden" />
自定义导航栏
使用 NavBarTemplate
NavBarTemplate 参数接收一个布尔值,表示当前是否为侧边栏模式:
<SxAppShell>
<NavBarTemplate>
@{ var isSidebarMode = context; }
<SxNavBar TitleText="我的应用"
IsSidebarMode="@isSidebarMode"
ShowHamburger="@isSidebarMode">
<LeftActions>
@if (!isSidebarMode)
{
<SxButton Appearance="ButtonAppearance.Stealth"
IconStart="bars"
OnClick="ToggleSidebar" />
}
</LeftActions>
<FunctionalMenu>
<SxMenuItem Text="设置" Icon="gear" />
<SxMenuItem Text="退出" Icon="sign-out" />
</FunctionalMenu>
</SxNavBar>
</NavBarTemplate>
<Navigation>
<SxNavMenu>...</SxNavMenu>
</Navigation>
</SxAppShell>
自定义导航菜单
<SxAppShell>
<Navigation>
<SxNavMenu>
<!-- 普通链接 -->
<SxNavMenuLink Text="首页" IconName="house" Href="/" />
<!-- 分组 -->
<SxNavMenuGroup Text="用户管理" IconName="users" DefaultOpen="true">
<SxNavMenuLink Text="用户列表" Href="/users" />
<SxNavMenuLink Text="角色管理" Href="/roles" />
<SxNavMenuLink Text="权限设置" Href="/permissions" />
</SxNavMenuGroup>
<!-- 带徽标 -->
<SxNavMenuLink Text="消息" IconName="envelope" Href="/messages">
<Badge>
<SxBadge>5</SxBadge>
</Badge>
</SxNavMenuLink>
<!-- 分隔线 -->
<SxNavMenuDivider />
<!-- 外部链接 -->
<SxNavMenuLink Text="帮助文档"
IconName="question-circle"
Href="https://docs.example.com"
Target="_blank" />
</SxNavMenu>
</Navigation>
</SxAppShell>
底部栏 (BottomBar)
SxAppShell 提供了一个 BottomBar RenderFragment 插槽,用于在侧边栏底部放置固定内容。这是一个通用容器,可以放置任意组件,最常见的用途是放置 SxUserBar 用户栏组件。
设计理念
- 容器与内容分离:BottomBar 只是一个容器,不包含业务逻辑
- 固定定位:BottomBar 固定在侧边栏底部,不随导航内容滚动
- 毛玻璃效果:内置半透明背景和模糊效果,滚动内容从其下方穿过
- 灵活组合:可放置 SxUserBar、自定义按钮或任何其他组件
基本用法
<SxAppShell>
<Navigation>
<SxNavMenu>
<SxNavMenuLink Text="首页" IconName="house" Href="/" />
<SxNavMenuLink Text="设置" IconName="gear" Href="/settings" />
</SxNavMenu>
</Navigation>
<BottomBar>
<SxUserBar UserName="张三"
UserSubtitle="admin@example.com"
UserPresence="AvatarPresence.Online"
OnLogout="HandleLogout"
OnSettings="HandleSettings" />
</BottomBar>
</SxAppShell>
@code {
private async Task HandleLogout()
{
await AuthService.LogoutAsync();
NavigationManager.NavigateTo("/login");
}
private void HandleSettings()
{
NavigationManager.NavigateTo("/settings");
}
}
SxUserBar 配置
SxUserBar 是一个独立组件,自动响应 SxAppShell 的折叠状态:
<SxAppShell @bind-Collapsed="_collapsed">
<Navigation>...</Navigation>
<BottomBar>
<SxUserBar UserName="张三"
UserSubtitle="admin@example.com"
UserAvatarUrl="/avatars/user.jpg"
ShowSettings="true"
ShowQuickSettings="true"
ShowLogout="true"
OnLogout="HandleLogout"
OnSettings="HandleSettings"
OnUserClick="HandleUserClick">
<MenuContent>
<!-- 自定义菜单项(显示在内置项之前) -->
<SxMenuItem Text="我的订单" Icon="shopping-cart" OnClick="..." />
<SxMenuItem Text="消息中心" Icon="bell" OnClick="..." />
<SxMenuDivider />
</MenuContent>
</SxUserBar>
</BottomBar>
</SxAppShell>
SxUserBar 参数
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
UserName |
string? |
"User" |
用户名 |
UserSubtitle |
string? |
null |
副标题(邮箱/角色) |
UserAvatarUrl |
string? |
null |
头像 URL |
UserPresence |
AvatarPresence |
None |
在线状态(默认不显示) |
MenuContent |
RenderFragment? |
null |
自定义菜单项 |
ShowSettings |
bool |
true |
显示设置菜单项 |
ShowQuickSettings |
bool |
true |
显示快捷设置(主题/语言) |
ShowLogout |
bool |
true |
显示退出登录菜单项 |
OnLogout |
EventCallback |
退出登录点击事件 | |
OnSettings |
EventCallback |
设置点击事件 | |
OnUserClick |
EventCallback |
用户信息区域点击事件 |
交互行为
SxUserBar 分为两个独立的可点击区域:
- 用户信息区域(头像 + 用户名):点击触发
OnUserClick事件,可用于跳转到用户资料页 - 箭头按钮:点击弹出菜单,箭头图标会旋转指示菜单状态
<SxUserBar UserName="张三"
OnUserClick="@(() => Nav.NavigateTo("/profile"))"
OnLogout="HandleLogout"
OnSettings="HandleSettings" />
折叠状态适配
SxUserBar 通过 CascadingParameter 接收 SxAppShellContext,自动响应折叠状态:
- 展开状态:显示头像、用户名、副标题和箭头按钮
- 折叠状态:仅显示头像,点击头像弹出菜单
自定义底部栏内容
BottomBar 可以放置任意内容,不限于 SxUserBar:
<SxAppShell>
<Navigation>...</Navigation>
<BottomBar>
<!-- 自定义底部栏:版本信息 -->
<div style="padding: var(--sx-spacing-m) var(--sx-shell-rail-pad-x); text-align: center;">
<SxTypography Variant="TypographyVariant.Caption">v1.0.0</SxTypography>
</div>
</BottomBar>
</SxAppShell>
毛玻璃效果
BottomBar 容器自带毛玻璃效果(backdrop-filter),当导航菜单滚动时,内容会从底部栏下方透过:
.sx-sidebar-bottom-bar {
background: color-mix(in srgb, var(--sx-colorNeutralBackground2) 80%, transparent);
backdrop-filter: blur(12px);
}
内部导航
SxAppShell 支持内部导航模式,页面切换在外壳内部完成,不触发浏览器导航。
基于 NavigationContent
<SxAppShell NavigationMode="SxAppShellNavigationMode.Internal"
InitialRoute="/dashboard"
OnNavigate="HandleNavigate">
<Navigation>
<SxNavMenu>
<SxNavMenuLink Text="仪表盘" IconName="chart-line" Href="/dashboard" />
<SxNavMenuLink Text="用户" IconName="users" Href="/users" />
</SxNavMenu>
</Navigation>
<NavigationContent>
@{ var route = context; }
@switch (route)
{
case "/dashboard":
<DashboardPage />
break;
case "/users":
<UsersPage />
break;
default:
<NotFoundPage />
break;
}
</NavigationContent>
</SxAppShell>
@code {
private void HandleNavigate(string? route)
{
Console.WriteLine($"导航到: {route}");
}
}
基于路由程序集
<SxAppShell NavigationMode="SxAppShellNavigationMode.Auto"
RouteAppAssembly="@typeof(Program).Assembly"
InitialRoute="/dashboard">
<Navigation>
<SxNavMenu>
<SxNavMenuLink Text="仪表盘" Href="/dashboard" />
<SxNavMenuLink Text="用户" Href="/users" />
</SxNavMenu>
</Navigation>
</SxAppShell>
浏览器 URL 同步
<!-- 默认:同步浏览器 URL -->
<SxAppShell SyncBrowserUrl="true" />
<!-- 禁用同步(适用于嵌套或演示场景) -->
<SxAppShell SyncBrowserUrl="false" />
历史栈限制
<SxAppShell MaxHistoryDepth="10"
MaxCachedPages="20">
<!-- 每个 Tab 最多保留 10 条历史记录 -->
<!-- 全局最多缓存 20 个页面 -->
</SxAppShell>
Blazor Server 预渲染
ServerPrerendered 模式
在使用 render-mode="ServerPrerendered" 时,必须为 SxAppShell 提供稳定的 Id 参数,以确保服务端预渲染和客户端水合使用相同的 ID:
<!-- _Layout.cshtml -->
<body>
<component type="typeof(AppShell)" render-mode="ServerPrerendered" />
<script src="/_framework/blazor.server.js"></script>
</body>
<!-- AppShell.razor -->
<SxAppRoot>
<SxAppShell Id="main-shell"
ShowCollapseToggle="true"
@bind-Collapsed="_collapsed"
TitleText="我的应用">
<Navigation>
<SxNavMenu>
<SxNavMenuLink Text="首页" IconName="house" Href="/" />
</SxNavMenu>
</Navigation>
<Content>
@(isWide => @<main id="content-slot"></main>)
</Content>
</SxAppShell>
</SxAppRoot>
@code {
private bool _collapsed;
}
注意事项
- 必须提供
Id参数:避免预渲染和水合时 ID 不一致导致事件处理器失效 - 确保服务注册:
IUserPreferences、INavStackService等服务必须在 DI 容器中注册 - 避免依赖 JS 初始状态:断点检测等 JS 功能在预渲染时不可用,组件会在水合后初始化
不同渲染模式对比
| 渲染模式 | 描述 | 注意事项 |
|---|---|---|
ServerPrerendered |
HTML 预渲染 + SignalR 交互 | 需要稳定 Id |
Server |
仅 SignalR 交互 | 无预渲染闪烁,首屏稍慢 |
WebAssembly |
客户端渲染 | 无特殊注意事项 |
WebAssemblyPrerendered |
HTML 预渲染 + WASM 交互 | 需要稳定 Id |
嵌套 Shell
SxAppShell 支持嵌套使用(如演示场景)。嵌套的 Shell 会自动禁用浏览器 URL 同步:
<SxAppShell Id="outer-shell">
<Content>
@(isWide => @<div class="demo-container">
<!-- 嵌套的 Shell,不会影响浏览器 URL -->
<SxAppShell Id="inner-shell"
SyncBrowserUrl="false"
SizingMode="SxAppShellSizingMode.Container">
...
</SxAppShell>
</div>)
</Content>
</SxAppShell>
API
Parameters
| 参数名 | 类型 | 默认值 | 描述 |
|---|---|---|---|
Id |
string? |
null |
组件 ID,ServerPrerendered 模式必须提供 |
Navigation |
RenderFragment? |
null |
侧边栏导航内容 |
NavBarTemplate |
RenderFragment<bool>? |
null |
导航栏模板(参数为是否侧边栏模式) |
Content |
RenderFragment<bool>? |
null |
主内容模板(参数为是否宽屏) |
TitleText |
string? |
null |
默认导航栏标题文本 |
ShowCollapseToggle |
bool |
true |
是否显示内置折叠切换按钮 |
Collapsed |
bool |
false |
侧边栏折叠状态 |
CollapsedWidth |
string? |
null |
折叠宽度(如 "60px") |
CollapsedRailMode |
SxAppShellCollapsedRailMode |
Auto |
窄条显示策略 |
AlwaysShowSidebar |
bool? |
null |
是否始终显示侧边栏(小屏幕也显示窄条) |
ShowContentNavBarWhenCollapsed |
bool |
true |
折叠时是否显示内容区导航栏 |
SizingMode |
SxAppShellSizingMode |
Viewport |
尺寸策略(Viewport/Container) |
LayoutMode |
SxAppShellLayoutMode? |
null |
布局模式(null=自动, Sidebar, Drawer) |
BreakpointConfig |
SxAppShellBreakpointConfig? |
null |
自定义断点配置 |
DrawerOverlayMode |
SxAppShellDrawerOverlayMode |
Transparent |
抽屉遮罩模式 |
EnableSafeArea |
bool |
true |
是否启用安全区(移动端) |
NavigationMode |
SxAppShellNavigationMode |
Auto |
导航模式 |
NavigationContent |
RenderFragment<string?>? |
null |
内部导航内容模板 |
RouteAppAssembly |
Assembly? |
null |
内部路由程序集 |
RouteAdditionalAssemblies |
IEnumerable<Assembly>? |
null |
额外路由程序集 |
RouteDefaultLayout |
Type? |
null |
默认布局类型 |
RouteFound |
RenderFragment<RouteData>? |
null |
路由匹配模板 |
RouteNotFound |
RenderFragment? |
null |
路由未匹配模板 |
InitialRoute |
string? |
null |
初始路由路径 |
SyncBrowserUrl |
bool |
true |
是否同步浏览器 URL |
MaxHistoryDepth |
int? |
null |
单 Tab 历史栈深度限制 |
MaxCachedPages |
int? |
null |
缓存页面数量限制 |
BottomBar |
RenderFragment? |
null |
底部栏内容(固定在侧边栏底部,可放置 SxUserBar 等组件) |
Events
| 事件名 | 类型 | 描述 |
|---|---|---|
BreakpointChanged |
EventCallback<Breakpoint> |
断点变化时触发 |
CollapsedChanged |
EventCallback<bool> |
折叠状态变化时触发 |
OnNavigate |
EventCallback<string?> |
内部导航时触发 |
OnBack |
EventCallback |
返回时触发 |
OnHome |
EventCallback |
返回首页时触发 |
Methods
| 方法名 | 返回值 | 描述 |
|---|---|---|
ToggleSidebar() |
void |
切换侧边栏折叠状态 |
OnBreakpointChanged(string) |
void |
JS 调用的断点变化回调 |
枚举类型
public enum Breakpoint { XS, SM, MD, LG, XL }
public enum SxAppShellSizingMode { Viewport, Container }
public enum SxAppShellLayoutMode { Auto, Sidebar, Drawer }
public enum SxAppShellCollapsedRailMode { Auto, Show, Hidden }
public enum SxAppShellDrawerOverlayMode { Transparent, Hidden }
public enum SxAppShellNavigationMode { Auto, Internal, External }
常见问题
Q: 点击折叠按钮无响应?
检查以下几点:
- ServerPrerendered 模式下是否提供了
Id参数 IUserPreferences和INavStackService服务是否已注册- 浏览器控制台是否有 JS 错误
Q: 断点变化不触发?
确保:
blazor.server.js或blazor.webassembly.js已正确加载- 组件已完成水合(预渲染模式)
BreakpointConfig配置正确
Q: 内部导航不工作?
检查:
NavigationMode是否正确设置NavigationContent或RouteAppAssembly是否提供SyncBrowserUrl设置是否符合预期