ant-design-blazor ReuseTabs 布局实战:多标签页导航与页面刷新机制解析
UI组件前端【免费下载链接】ant-design-blazorA rich set of enterprise-class UI components based on Ant Design and Blazor.项目地址https://gitcode.com/gh_mirrors/an/ant-design-blazor点击查看免费下载ReuseTabs 是 ant-design-blazor 提供的路由复用组件它以 Tabs 为基础实现应用内的页面标签与页面缓存让用户在多个已打开页面间自由切换而不丢失状态。本文围绕该功能的「布局」演示layout.md展开完整解析其配套实现 Layout_.razor如何把 ReuseTabs 嵌入 Layout 布局体系Sider 侧边导航 Header 顶栏 Content 内容区如何添加刷新当前页面按钮以及刷新与页面重建在源码层面的真实工作方式。读完本文你将掌握在自有 Blazor 项目中集成 ReuseTabs 布局、动态切换级联 RouteData、以及实现无浏览器刷新的页面重载的完整方案。ReuseTabs 的定位给应用加上浏览器式的页面标签按官方介绍ReuseTabs 的作用是利用选项卡切换组件实现应用内的页面标签与页面缓存适用于两种典型场景见 ReuseTabs 文档需要使用应用内页面标签保留打开过的页面需要缓存页面状态返回已打开页面时状态不丢失。而布局演示要解决的是第三个问题如何让这些标签页与 Layout 布局侧边栏、顶栏、面包屑、内容区和谐共存并支持一键刷新当前页。这正是企业中后台系统最常见的形态——左侧导航切换路由、顶部提供全局操作、中间内容区以多标签页形式承载各路由页面。布局示例全景拆解Layout_.razor演示页面Layout_.razor继承自LayoutComponentBase整体结构可以分成五块Sider 导航、Header 顶栏、Breadcrumb 面包屑、ReuseTabs 核心区域与局部样式。1. 侧边栏Sider Menu 提供路由导航inherits LayoutComponentBase Layout Idreuse-tabs-layout Sider NoTrigger div classlogo / Menu ThemeMenuTheme.Dark ModeMenuMode.Inline MenuItem Iconhome RouterLink/reuseHome/MenuItem MenuItem Iconsetting RouterLink/reuse/title-attributePage settings/MenuItem MenuItem Iconblock RouterLink/reuse/dynamic-titleDynamic title/MenuItem MenuItem Iconuser RouterLink/reuse/singleton RouterMatchNavLinkMatch.PrefixSingleton page/MenuItem MenuItem Iconuser RouterLink/reuse/ignoreIgnore/MenuItem /Menu /Sider ... /Layout侧边栏使用Menu的MenuMode.Inline模式每个MenuItem通过RouterLink绑定演示路由。值得注意的细节是RouterMatchNavLinkMatch.Prefix单例页面/reuse/singleton可能携带不同参数使用前缀匹配可以让带参数的子路由也保持菜单高亮。2. 顶栏刷新按钮 级联 RouteData 开关Header Classsite-layout-background Stylepadding: 0; Icon TypeIconType.Outline.Reload Classtrigger OnClickReload / Cascading RouteData: Switch ValueRoutes.CascadingRouteData OnChangeCascadingChange/Switch /HeaderHeader 右侧放了一个IconType.Outline.Reload图标作为刷新当前页按钮点击时调用Reload()左侧是一个Switch用于动态开关是否级联传入 RouteData。对应的代码块如下inject ReuseTabsService ReuseTabsService inject NavigationManager NavigationManager code { [CascadingParameter] private Routes Routes { get; set; } void Reload() { ReuseTabsService.ReloadPage(); } void CascadingChange(bool value) { ReuseTabsService.CloseAll(); Routes.SetCascadingRouteData(value); } }这是理解整个布局演示的关键ReloadPage()是刷新当前页按钮的幕后英雄SetCascadingRouteData()则控制 ReuseTabs 是否能够捕获并缓存页面。两者的底层原理在下一节详解。3. 面包屑AutoGenerate 自动生成Breadcrumb AutoGenerate BreadcrumbItem Href/reuseHome/BreadcrumbItem /BreadcrumbBreadcrumb的AutoGenerate属性会依据当前路由自动生成面包屑层级BreadcrumbItem Href/reuseHome/BreadcrumbItem作为固定首页项。这样每个标签页内容上方都会展示对应的导航路径符合中后台的常见信息架构。4. 核心区域ReuseTabs 承载多标签页ReuseTabs Stylemargin: 16px; Body Body /Body TabPaneTemplate Content Classsite-layout-background Stylepadding: 24px;min-height: 280px; context.Body /Content /TabPaneTemplate /ReuseTabs这一段有两个关键参数对应 ReuseTabs.razor.cs 中的定义BodyRenderFragment类型用于设置 Layout 组件的 Body。在无级联 RouteData 模式下必须设置——因为此时 ReuseTabs 拿不到路由信息来生成页面内容只能把 Layout 的Body直接渲染出来兜底。演示中显式传入了BodyBody的等价写法BodyBody/Body。TabPaneTemplateRenderFragmentReuseTabsPageItem类型默认值是context context.Body原样输出页面内容。演示将其覆盖为在页面外包一层Content加上背景色、内边距padding: 24px;和最小高度min-height: 280px;从而让每个标签页的内容区在视觉上与顶栏、侧边栏区分开。传入的context即ReuseTabsPageItem其中的Body就是当前页面组件的内容片段定义见 ReuseTabsPageItem.cs。5. 局部样式让布局细节可见style #reuse-tabs-layout .trigger { font-size: 18px; line-height: 64px; padding: 0 24px; cursor: pointer; transition: color 0.3s; } #reuse-tabs-layout .trigger:hover { color: #1890ff; } #reuse-tabs-layout .logo { height: 32px; background: rgba(255, 255, 255, 0.2); margin: 16px; } #reuse-tabs-layout .site-layout .site-layout-background { background: #fff; } #reuse-tabs-layout .ant-tabs-content { margin-top: -16px; border: 1px solid #f0f0f0; } /style样式通过#reuse-tabs-layout限定作用域避免污染全局刷新图标居中对齐并带 hover 高亮logo 占位块呈现半透明白色内容区背景为白色ant-tabs-content通过负 margin 与边框勾勒出标签内容区与顶栏的边界。刷新当前页的底层原理无需刷新浏览器的页面重建ReloadPage()是刷新当前页面按钮的核心其实现位于 ReuseTabsService.cspublic void ReloadPage() { ReloadPage(null); } public void ReloadPage(string url) { url ?? CurrentUrl; var reuseTabsPageItem _pages?.FirstOrDefault(w w.Url url); reuseTabsPageItem.Body null; reuseTabsPageItem.CachedRenderFragment null; // Clear cached fragment to force re-render reuseTabsPageItem.Rendered false; // only reload current page, and other page would be load by tab navigation if (reuseTabsPageItem?.Key ActiveKey) { StateHasChanged(); } }从源码可以看到刷新做了三件事Body null与CachedRenderFragment null清空页面内容与缓存片段强制后续重新生成渲染树Rendered false标记页面未渲染使组件进入待重建状态仅当目标页是当前激活页Key ActiveKey时立即触发StateHasChanged()非当前标签页则留待切换到它时再加载。重建动作发生在 ReuseTabs.razor.cs 的OnAfterRender中// reload current page after ReloadPage() was called by ReuseTabsService if (ReuseTabsService.CurrentPage.Rendered false) { LoadPage(); StateHasChanged(); // update title need calling render again }LoadPage()内部调用ReuseTabsService.TrySetRouteData(ActualRouteData, true)见 ReuseTabsService.cs它依据当前 URL 找到或新建ReuseTabsPageItem重新设置RouteData与Body。页面内容的实际构建由CreateBody完成——它通过builder.OpenComponent(routeData.PageType)打开目标页面组件并把路由参数逐个AddAttribute传入ReuseTabsService.cs。也就是说点击刷新按钮后页面组件会被销毁并重新实例化重新执行OnInitialized等生命周期方法但浏览器地址栏不变、整个应用不做整页刷新。这正是重新加载指定标签的页面让页面组件重新初始化且无需刷新浏览器这一 API 承诺的实现依据。此外ReuseTabs在渲染每个TabPane时会把刷新/关闭/关闭全部/关闭其他等操作注入标签右键菜单见 ReuseTabs.razor其中刷新同样调用ReuseTabsService.ReloadPage(itemUrl)与顶栏按钮殊途同归。级联 RouteData 开关布局如何决定是否捕获页面布局演示中 Header 的 Switch 绑定到Routes.CascadingRouteData这是演示站点自定义的Routes组件Routes.razor暴露的属性Found ContextrouteData if (CascadingRouteData) { CascadingValue ValuerouteData RouteView RouteDatarouteData DefaultLayouttypeof(MainLayout) / /CascadingValue } else { RouteView RouteDatarouteData DefaultLayouttypeof(MainLayout) / } ... /Foundpublic bool CascadingRouteData { get; set; } true; public void SetCascadingRouteData(bool value) { CascadingRouteData value; StateHasChanged(); }SetCascadingRouteData(true)时RouteView被CascadingValue ValuerouteData包裹ReuseTabs通过[CascadingParameter] private RouteData RouteData见 ReuseTabs.razor.cs拿到当前路由信息从而捕获页面、生成标签、缓存状态SetCascadingRouteData(false)时RouteView直接渲染ReuseTabs 拿不到 RouteData只能依赖显式传入的Body。布局演示中CascadingChange的完整语义是切换开关时先ReuseTabsService.CloseAll()清空所有已打开的标签再切换级联状态——避免在模式切换时残留旧标签页导致状态错乱。从源码看CloseAll()会关闭所有Closabletrue且未Pin的页面ReuseTabsService.cs因此在配合开关切换时需要保证普通页面可关闭、固定页面Pin可保留。顺带一提动态演示页 NonRouteData.razor 展示了不级联 RouteData也能工作的场景它在OnInitialized中调用Routes.SetCascadingRouteData(false)关闭级联随后用ReuseTabsService.UpdatePage(...)动态修改当前标签的标题、顺序与可关闭性——这正是无级联 RouteData 时用UpdatePage动态设置页面的用法。在自有项目中落地两种接入方式方式一仅需多标签页无级联 RouteData按 ReuseTabs 文档 的说明只需修改MainLayout.razor加入ReuseTabs组件即可注意不级联传入 RouteData 时Body是必须的inherits LayoutComponentBase div classpage div classsidebar NavMenu / /div div classmain ReuseTabs Classtop-row px-4 TabPaneClasscontent px-4 BodyBody / /div /divClass作用于 Tabs 容器TabPaneClass作用于每个 Pane 容器Body传入 Layout 的 Body。这一步即可获得基础的多标签页体验。方式二需要页面级配置级联 RouteData如果需要使用ReuseTabsPageAttribute或IReuseTabsPage例如设置固定标题、固定标签、忽略页面、单例模式等必须级联传入 RouteData否则这些特性无效。需要额外修改项目中的Routes.razor用CascadingValue ValuerouteData包裹RouteView或AuthorizeRouteViewRouter AppAssemblytypeof(Program).Assembly Found ContextrouteData CascadingValue ValuerouteData RouteView RouteDatarouteData DefaultLayouttypeof(MainLayout) / /CascadingValue /Found ... /Router级联之后ReuseTabs就能自动捕获每个路由页面生成标签ReuseTabsPageAttribute的Title、Pin、Ignore、Singleton、Closable、KeepAlive、Order等属性生效页面还可以实现IReuseTabsPage.GetPageTitle()返回动态标题参考 DynamicTitle.razor标题变化后调用ReuseTabsService.Update()刷新标签显示。布局演示正是采用了这一增强模式演示站点的 Routes.razor 根据CascadingRouteData开关决定是否用CascadingValue包裹RouteView而 Layout_.razor 中的每个演示页面如page /reuse/singleton都通过layout指令指定使用该布局。与布局相关的 API 速览围绕布局与刷新场景最常用的 API 如下完整表格见 ReuseTabs 文档 的 API 章节与 ReuseTabsService.cs 的公开方法一一对应API说明在布局中的典型用途ReuseTabs.Body设置 Layout 组件的 Body无级联 RouteData 时必须设置传入Body兜底渲染ReuseTabs.TabPaneTemplate自定义标签页内容模板上下文为ReuseTabsPageItem给页面包一层 Content 容器并设置背景与内边距ReuseTabs.TabPaneClass设置 Pane 容器类名配合全局样式统一标签内容区外观ReuseTabs.Draggable是否可拖拽调整标签顺序默认 false顶部标签条交互增强ReuseTabs.HidePages是否隐藏页面内容、仅保留标题标签与ReusePages搭配自定义内容区ReuseTabsService.ReloadPage(url)重新加载指定标签页面组件重新初始化且无需刷新浏览器不传 url 时重载当前页顶栏刷新当前页按钮ReuseTabsService.CloseAll()关闭所有可关闭且未固定的页面切换级联 RouteData 模式前清空标签ReuseTabsService.CloseCurrent()/CloseOther(url)/ClosePage(url)关闭当前页 / 除指定页外全部 / 指定页标签右键菜单与业务操作ReuseTabsService.Update()更新标签状态动态标题引用的变量变化后调用动态标题页面ReuseTabsService.UpdatePage(action)动态修改当前页面的属性无级联 RouteData 时的标题/顺序/可关闭性设置ReuseTabsPageAttribute.Ignore页面不显示在标签中整页展示如登录页配合不含 ReuseTabs 的独立 Layout 打开全新页面ReuseTabsPageAttribute.Pin / PinUrl启动时固定加载该页面用于主页或默认页布局初始化即保留首页标签ReuseTabsPageAttribute.Singleton相同路由模板复用同一页面参数不同不新开标签详情类页面避免标签爆炸其中Pin的实现细节值得说明ReuseTabsService.Init会调用ScanPinnedPageAttribute扫描程序集中所有Pintrue的页面按PinUrl或RouteAttribute模板预创建标签ReuseTabsService.cs。可通过ReuseTabs的ScanAssemblies参数指定扫描的程序集默认扫描当前页面所在程序集传入空数组可禁用扫描见 ReuseTabs.razor.cs。测试与验证服务行为有据可查仓库在 ReuseTabsServiceTests.cs 中为ReuseTabsService的关键行为提供了单元测试可用于验证上文提到的机制Init_WithDynamicAssemblyInAppDomain_DoesNotThrow扫描程序集时遇到动态程序集不抛异常对ScanPinnedPageAttribute的健壮性回归测试Init_WithNullScanAssemblies_DoesNotScanPinnedPages与Init_WithEmptyScanAssemblies_DoesNotScanPinnedPages无程序集列表时不扫描固定页面Init_WithSpecifiedScanAssemblies_UsesProvidedAssemblyList传入指定程序集后可识别出[Route(/fake-pinned)]与[ReuseTabsPage(Pin true, PinUrl /fake-pinned)]标注的页面验证了 Pin 页扫描链路ReuseTabs_DefaultScanAssemblies_UsesPageAssembly默认使用页面所在程序集进行扫描。此外测试应用 MainLayout.razor 提供了真实集成样例在 Layout 的 Header 中放置圆形 Reload 按钮Reload()调用ReuseTabsService.ReloadPage()内容区直接放置ReuseTabs Draggable SizeTabSize.Small /并注释了AuthorizeView模式下对登录态与多标签页的处理思路。注意事项与踩坑提示不级联 RouteData 时必须设置Body否则 Layout 的页面内容无处渲染这是最容易遗漏的一点。Ignoretrue的页面仍需配合不含 ReuseTabs 的 LayoutReuseTabs 会捕获页面组件但不渲染 Layout因此希望整页打开如登录页时需要让该页面使用一个不含 ReuseTabs 的独立 Layout见 Ignore.md。单例页面不会重新实例化Singletontrue的页面被不同参数复用时不会重新执行OnInitialized需要监听NavigationManager.LocationChanged导航事件来更新界面见 singleton.md。KeepAlive依赖渲染片段引用稳定ReuseTabs渲染时通过CachedRenderFragment缓存页面片段以保证引用稳定见 ReuseTabs.razor而ReloadPage会清空该缓存以强制重建——两者配合才能实现缓存页面与强制刷新的切换。刷新按钮只对当前激活标签生效ReloadPage()默认重载当前页对其他标签页调用ReloadPage(url)时源码会在该标签被激活时才真正重建。综上布局演示虽只有寥寥数语但其背后是一套完整的Layout ReuseTabs ReuseTabsService协作机制Layout 负责整体骨架ReuseTabs 负责标签页捕获与缓存ReloadPage与SetCascadingRouteData分别提供页面级刷新与模式级切换能力。将这套模式迁移到自有项目时只需按照MainLayout 放置 ReuseTabs、Routes.razor 级联 RouteData、按需注入 ReuseTabsService三步走即可获得企业级的多标签页应用体验。赞分享UI组件前端【免费下载链接】ant-design-blazorA rich set of enterprise-class UI components based on Ant Design and Blazor.项目地址https://gitcode.com/gh_mirrors/an/ant-design-blazor点击查看免费下载相关推荐ant-design-blazor ReuseTabs 单例页面Singleton机制参数复用、生命周期与导航刷新实战ant design blazor ReuseTabs 单例页面Singleton机制参数复用、生命周期与导航刷新实战 ReuseTabs 是 ant d前端UI组件设计系统ant-design-blazor ReuseTabs 实战指南应用内多标签页与页面状态缓存ant design blazor ReuseTabs 实战指南应用内多标签页与页面状态缓存 ReuseTabs 是 ant design blazor 提供前端UI组件设计系统ant-design-blazor 的 ReuseTabs 路由复用组件实战指南多标签页与页面缓存ant design blazor 的 ReuseTabs 路由复用组件实战指南多标签页与页面缓存 ReuseTabs路由复用是 ant design b前端UI组件设计系统上一篇new-api 前端样式规范解析shadcn/ui 语义化颜色与 Tailwind 类名编写准则下一篇OpenLyricsfoobar2000开源歌词插件的专业使用指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考