ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

在 Refine 中使用 ThemedLayout 搭建 Ant Design 管理后台布局

在 Refine 中使用 ThemedLayout 搭建 Ant Design 管理后台布局 在 Refine 中使用 ThemedLayout 搭建 Ant Design 管理后台布局【免费下载链接】refineA React Framework for building internal tools, admin panels, dashboards B2B apps with unmatched flexibility.项目地址: https://gitcode.com/GitHub_Trending/re/refine本文讲解 Refine 内置的ThemedLayout布局组件它以 Ant Design 的Layout/Sider为基础提供 Header、Sider、Title、Footer、OffLayoutArea 五个可插拔区域并内置响应式适配与主题联动。读完本文你将掌握ThemedLayout的接入方式、全部 Props 的配置要点、如何用swizzle弹出源码深度定制以及如何通过useThemedLayoutContext在任意页面控制侧边栏的折叠状态。什么是 ThemedLayoutThemedLayout是 Refine 与 Ant Design 集成包refinedev/antd提供的开箱即用布局组件。它基于 Ant Design 的Layout和Sider组件来定义页面的整体骨架同时把页面拆分为五个可独立替换或定制的小节ThemedHeader显示在页面顶部可展示当前用户的姓名与头像。ThemedSider显示在页面左侧根据 resources 配置自动生成菜单项。ThemedTitle显示在ThemedSider顶部包含图标与文字。Footer显示在页面底部Refine 不提供默认实现需自行传入。OffLayoutArea渲染在主布局之外可放在页面任意位置同时仍属于整体布局的一部分。由于这五个区域都通过 Props 注入使用ThemedLayout可以在多个页面或站点分区之间保持一致的视觉与结构同时提升代码的可维护性与复用性。从源码实现看packages/antd/src/components/themedLayout/index.tsxThemedLayout的核心组装逻辑非常清晰通过Grid.useBreakpoint()获取响应式断点并根据屏幕尺寸动态调整内容区 padding大屏 24px小屏 12px外部容器AntdLayout设置minHeight: 100vh保证页面始终撑满视口内部再嵌套一层AntdLayout承载 Header 与 Content整个布局用ThemedLayoutContextProvider包裹为 Sider 折叠状态提供全局上下文。组件的 Props 类型定义在 packages/antd/src/components/themedLayout/types.ts 中其中RefineThemedLayoutSiderProps在refinedev/ui-types的基础上额外扩展了fixed属性。快速上手接入 ThemedLayout下面是一个完整的可用示例使用refinedev/react-router作为路由、refinedev/simple-rest作为数据提供者并用RefineThemes.Blue配置 Ant Design 主题。这是ThemedLayout最常见的接入形态——把它作为路由布局组件包裹Outlet /import { Refine } from refinedev/core; import { ThemedLayout, RefineThemes } from refinedev/antd; import { ConfigProvider } from antd; import { AntdInferencer } from refinedev/inferencer/antd; import routerProvider from refinedev/react-router; import { BrowserRouter, Routes, Route, Outlet } from react-router; import dataProvider from refinedev/simple-rest; import { authProvider } from ./authProvider; const API_URL https://api.fake-rest.refine.dev; const App: React.FC () { return ( BrowserRouter ConfigProvider theme{RefineThemes.Blue} Refine routerProvider{routerProvider} dataProvider{dataProvider(API_URL)} authProvider{authProvider} resources{[ { name: samples, list: /samples, }, ]} Routes Route element{ ThemedLayout Outlet / /ThemedLayout } Route path/samples element{AntdInferencer /} / /Route /Routes /Refine /ConfigProvider /BrowserRouter ); };其中authProvider提供登录态与用户身份信息供 Header 展示姓名和头像const authProvider { login: async () ({ success: true, redirectTo: /, }), logout: async () ({ success: true, redirectTo: /login, }), onError: async (error) { console.error(error); return { error }; }, check: async () ({ authenticated: true, }), getIdentity: async () ({ id: 1, name: Jane Doe, avatar: https://unsplash.com/photos/IWLOvomUmWU/download?forcetruew640, }), };ThemedLayout是响应式的在平板等小屏场景下Sider 会切换为 Ant Design 的Drawer抽屉形式在大屏下则使用标准的Sider。这一判断逻辑位于 Sider 源码中sider/index.tsx通过Grid.useBreakpoint()的lg断点判断!breakpoint.lg即视为移动端。上述示例使用的是 React Router仓库中的 examples/auth-antd/src/App.tsx 就是同样模式的生产级参考实现。其他框架下思路一致Next.js 中放在src/app/layout.tsx作为页面布局Remix 中则放在路由布局文件如app/routes/_protected.tsx内包裹Outlet /。Sider默认侧边栏与自定义替换在ThemedLayout中侧边栏默认由ThemedSider渲染。该组件通过useMenuhook 根据Refine中声明的resources自动生成菜单项因此只要声明了 resourcesSider 就会自动生成对应的导航菜单。如果你需要完全替换侧边栏可以通过Siderprop 传入自定义组件import { Refine } from refinedev/core; import { ThemedLayout } from refinedev/antd; import { CustomSider } from ./CustomSider; const App: React.FC () { return ( Refine // ... ThemedLayout Sider{() CustomSider /} {/* ... */} /ThemedLayout /Refine ); };除了整体替换也可以保留默认ThemedSider通过它的 Props 或swizzle进行局部定制。例如用render与Title定制菜单渲染逻辑和顶部标题import { Refine } from refinedev/core; import { ThemedLayout, ThemedSider } from refinedev/antd; import { CustomTitle } from ./CustomTitle; const App: React.FC () { return ( Refine // ... ThemedLayout Sider{() ( ThemedSider Title{({ collapsed }) CustomTitle collapsed{collapsed} /} render{({ items, logout, collapsed }) { return ( divMy Custom Element/div {items} {logout} / ); }} / )} {/* ... */} /ThemedLayout /Refine ); };还可以通过fixed让侧边栏固定吸顶悬浮该属性可选默认falseimport { Refine } from refinedev/core; import { ThemedLayout, ThemedSider } from refinedev/antd; const App: React.FC () { return ( Refine // ... ThemedLayout Sider{() ThemedSider fixed /} {/* ... */} /ThemedLayout /Refine ); };fixed的实现细节可以从源码确认sider/index.tsx当fixed为true时Sider 会获得position: fixed; top: 0; height: 100vh; zIndex: 999的样式同时在其前方渲染一个占位 div展开时 200px、折叠时 80px避免内容被遮挡。Sider Props 一览Prop类型说明TitleReact.FC渲染在侧边栏顶部的组件即ThemedTitle或其替换件renderSiderRenderFunction自定义侧边栏内部菜单项与其他元素的渲染函数metaRecordstring, any创建菜单项路由时使用的元数据fixedboolean侧边栏是否固定activeItemDisabledboolean点击当前激活菜单项时是否禁用跳转避免重复刷新页面默认falseonSiderCollapsed(collapsed: boolean) void侧边栏折叠/展开时的回调siderItemsAreCollapsedboolean嵌套菜单项默认是展开还是折叠默认true折叠其中render的函数签名SiderRenderFunction如下type SiderRenderFunction (props: { items: JSX.Element[]; logout: React.ReactNode; dashboard: React.ReactNode; collapsed: boolean; }) React.ReactNode;值得说明的是activeItemDisabled在源码中通过给激活菜单项的链接设置pointerEvents: none实现sider/index.tsx而siderItemsAreCollapsed控制defaultOpenKeys的生成为false时所有菜单项的 key 都会被加入默认展开列表sider/index.tsx。控制初始折叠状态initialSiderCollapsedinitialSiderCollapsed用于设置ThemedSider的初始折叠状态true侧边栏默认折叠false侧边栏默认展开这也是默认值。ThemedLayout initialSiderCollapsed{true} {/* ... */} /ThemedLayout从上下文实现看contexts/themedLayoutContext/index.tsx该值作为useState的初始值注入即只影响首屏渲染后续折叠状态由用户交互驱动。监听折叠变化onSiderCollapsedonSiderCollapsed会在ThemedSider的collapsed状态变化时被触发。最常见的用途是把折叠状态持久化到localStorage下次进入页面时再通过initialSiderCollapsed恢复const MyLayout () { const onSiderCollapse (collapsed: boolean) { localStorage.setItem(siderCollapsed, collapsed); }; const initialSiderCollapsed Boolean(localStorage.getItem(siderCollapsed)); return ( ThemedLayout initialSiderCollapsed{initialSiderCollapsed} onSiderCollapsed{onSiderCollapse} {/* ... */} /ThemedLayout ); };源码层面ThemedLayoutContextProvider在setSiderCollapsed内部先更新内部 state再调用onSiderCollapsed回调contexts/themedLayoutContext/index.tsx因此回调一定能在状态变更后同步收到最新的collapsed值。Header用户信息展示与自定义ThemedLayout的头部默认由ThemedHeader渲染它使用useGetIdentityhook 获取当前用户信息并在头部右侧展示用户名与头像。需要说明的是ThemedHeader只有在getIdentity返回了name或avatar时才渲染源码见 header/index.tsx否则返回null顶部不会出现空白占位。替换默认 Header 同样简单import { Refine } from refinedev/core; import { ThemedLayout } from refinedev/antd; import { CustomHeader } from ./CustomHeader; const App: React.FC () { return ( Refine // ... ThemedLayout Header{() CustomHeader /} {/* ... */} /ThemedLayout /Refine ); };也可以保留默认实现并让它吸顶stickyimport { Refine } from refinedev/core; import { ThemedLayout, ThemedHeader, } from refinedev/antd; const App: React.FC () { return ( Refine // ... ThemedLayout Header{() ThemedHeader sticky /} {/* ... */} /ThemedLayout /Refine ); };sticky在源码中对应position: sticky; top: 0; zIndex: 1header/index.tsx滚动页面时头部会始终保持在视口顶部。Title品牌标识定制ThemedLayout顶部的标题默认由ThemedTitle渲染包含图标与文字并整体包裹在一个指向/的链接中。可以通过Titleprop 传入自定义实现例如根据折叠状态切换大小图标import { Refine } from refinedev/core; import { ThemedLayout, ThemedTitle } from refinedev/antd; import { MyLargeIcon, MySmallIcon } from ./MyIcon; const App: React.FC () { return ( Refine // ... ThemedLayout Title{({ collapsed }) ( ThemedTitle // collapsed 表示侧边栏是否折叠 collapsed{collapsed} icon{collapsed ? MySmallIcon / : MyLargeIcon /} textMy Project / )} {/* ... */} /ThemedLayout /Refine ); };ThemedTitle还有一个实用特性当没有显式传入icon和text时它会回退读取useRefineOptions()中配置的默认图标与文案title/index.tsx这让你可以在 Refine 全局配置中统一定义品牌标识各处的ThemedTitle自动生效。另外折叠状态下文字会被隐藏{!collapsed ...}只保留 24px 的图标。Footer自定义页脚Refine 不提供默认的 Footer 组件但你可以通过Footerprop 传入任意内容。下面的示例使用 Ant Design 的Layout.Footer实现一个居中显示的自定义页脚import { Refine } from refinedev/core; import { ThemedLayout } from refinedev/antd; import { Layout } from antd; const App: React.FC () { return ( Refine // ... ThemedLayout Footer{() ( Layout.Footer style{{ textAlign: center, color: #fff, backgroundColor: #7dbcea, }} My Custom Footer /Layout.Footer )} {/* ... */} /ThemedLayout /Refine ); };配合路由使用时效果与前面的基础示例相同只是在ThemedLayout上多传一个Footer渲染函数即可。从布局源码看Footer被放置在内容区之后、最内层AntdLayout的末尾index.tsx因此它天然位于整个页面内容的底部。OffLayoutArea布局之外的内容区OffLayoutArea用于渲染在主布局容器之外、但仍属于整体布局一部分的内容典型场景是悬浮按钮、反馈入口等。Refine 同样不提供默认实现需要自行传入import { Refine } from refinedev/core; import { ThemedLayout } from refinedev/antd; import { Button } from antd; const App: React.FC () { return ( Refine // ... ThemedLayout OffLayoutArea{() ( Button typeprimary sizesmall onClick{() alert(Off layout are clicked)} style{{ position: fixed, left: 8px, bottom: 8px, zIndex: 1000, }} Send us Feedback /Button )} {/* ... */} /ThemedLayout /Refine ); };在上面的示例中反馈按钮通过position: fixed固定在页面左下角即便它被声明在ThemedLayout内部实际渲染位置也独立于常规布局流源码见 index.tsx。使用 swizzle 深度自定义 该功能依赖refine/cli请先确保项目已安装。swizzle命令可以把ThemedLayout的源码“弹出”到你的项目src目录之后就可以直接修改源码实现任意定制。整个过程如下首先运行命令并选择要弹出的包 npm run refine swizzle ? Which package do you want to swizzle? (Use arrow keys or type to search) Data Provider ◯ refinedev/simple-rest UI Framework ◉ refinedev/antdRefine CLI 只会列出当前项目已安装的包。接着选择要弹出的组件? Which component do you want to swizzle? ◯ TagField ◯ TextField ◯ UrlField Other ◯ Breadcrumb ❯◉ ThemedLayout Pages ◯ ErrorPage ◯ AuthPage (Move up and down to reveal more choices)选择ThemedLayout后CLI 会在项目中生成如下文件Successfully swizzled Themed Layout Files created: - src/components/themedLayout/sider.tsx - src/components/themedLayout/header.tsx - src/components/themedLayout/title.tsx - src/components/themedLayout/index.tsx Warning: If you want to change the default layout; You should pass layout related components to the ThemedLayout/ components props.之后就可以从本地路径导入这些组件并自由组合import { Refine } from refinedev/core; import { ThemedLayout } from components/themedLayout; import { ThemedHeader } from components/themedLayout/header; import { ThemedSider } from components/themedLayout/sider; import { ThemedTitle } from components/themedLayout/title; const App () { return ( Refine /* ... */ ThemedLayout Header{ThemedHeader} Sider{ThemedSider} Title{ThemedTitle} /* ... */ /ThemedLayout /Refine ); };:::simple Good to knowRefine CLI 会根据你使用的框架决定生成目录例如使用 Remix 时路径会是app/components/layout。如果目标目录已存在同名文件swizzle 命令不会覆盖它。:::这一文件结构与refinedev/antd包内的源码布局一一对应——仓库中的 packages/antd/src/components/themedLayout 目录同样由sider/、header/、title/、index.tsx、types.ts组成并且配有对应测试如 index.spec.tsx 复用refinedev/ui-tests的layoutLayoutTests套件验证基础布局行为。因此 swizzle 弹出的实际上就是这份经过测试的官方实现。用 useThemedLayoutContext 控制折叠useThemedLayoutContexthook 用于在任意位置折叠/展开 Sider包括移动端的抽屉。你可以在任何页面中调用它例如在 Dashboard 页面放两个按钮来控制侧边栏import { Refine } from refinedev/core; import { ThemedLayout, RefineThemes, useThemedLayoutContext, } from refinedev/antd; import { ConfigProvider, Button, Space } from antd; import { AntdInferencer } from refinedev/inferencer/antd; import routerProvider from refinedev/react-router; import { BrowserRouter, Routes, Route, Outlet } from react-router; import dataProvider from refinedev/simple-rest; import { authProvider } from ./authProvider; const API_URL https://api.fake-rest.refine.dev; const DashboardPage () { const { siderCollapsed, setSiderCollapsed, mobileSiderOpen, setMobileSiderOpen, } useThemedLayoutContext(); return ( Space style{{ paddingTop: 30 }} Button typeprimary onClick{() setMobileSiderOpen(!mobileSiderOpen)} toggle mobile sider /Button Button typeprimary onClick{() setSiderCollapsed(!siderCollapsed)} toggle collapse of sider /Button /Space ); }; const App: React.FC () { return ( BrowserRouter ConfigProvider theme{RefineThemes.Blue} Refine routerProvider{routerProvider} dataProvider{dataProvider(API_URL)} authProvider{authProvider} resources{[ { name: dashboard, list: /, }, { name: samples, list: /samples, }, ]} Routes Route element{ ThemedLayout Outlet / /ThemedLayout } Route path/ element{DashboardPage /} / Route path/samples element{AntdInferencer /} / /Route /Routes /Refine /ConfigProvider /BrowserRouter ); };这个 hook 的实现非常轻量hooks/useThemedLayoutContext/index.ts它只是从ThemedLayoutContext中取出mobileSiderOpen、siderCollapsed、setMobileSiderOpen、setSiderCollapsed四个值并返回。Context 的默认值在 contexts/themedLayoutContext/index.tsx 中定义siderCollapsed与mobileSiderOpen初始均为false。也正因折叠状态由 Context 管理页面组件才能跨层级与 Sider 交互。FAQ如何持久化 Sider 的折叠状态问题刷新页面后如何恢复上次的折叠状态方案将initialSiderCollapsed的初始值来自localStorage或cookie同时用onSiderCollapsed把变化写回去。以下是三种路由框架下的写法。React Routersrc/App.tsximport { useState } from react; import { Refine } from refinedev/core; import { BrowserRouter, Routes, Route, Outlet } from react-router; import { ThemedLayout } from refinedev/antd; const App: React.FC () { // 该值可从 localStorage 或 cookie 中读取实现跨会话持久化 const [initialSiderCollapsed, setInitialSiderCollapsed] useState(true); return ( BrowserRouter Refine // ... {/* ... */} Routes Route element{ ThemedLayout initialSiderCollapsed{initialSiderCollapsed} Outlet / /ThemedLayout } {/* ... */} /Route /Routes /Refine /BrowserRouter ); }; export default App;Next.jspages/_app.tsximport { useState } from react; import { Refine } from refinedev/core; import { ThemedLayout } from refinedev/antd; import type { AppProps } from next/app; import type { NextPage } from next; function MyApp({ Component, pageProps }: AppProps): JSX.Element { // 该值可从 localStorage 或 cookie 中读取实现跨会话持久化 const [initialSiderCollapsed, setInitialSiderCollapsed] useState(true); const renderComponent () { if (Component.noLayout) { return Component {...pageProps} /; } return ( ThemedLayout initialSiderCollapsed{initialSiderCollapsed} Component {...pageProps} / /ThemedLayout ); }; return ( Refine // ... {/* ... */} {renderComponent()} /Refine ); } export default MyApp;Remixapp/routes/_layout.tsximport { useState } from react; import { Outlet } from remix-run/react; import { ThemedLayout } from refinedev/antd; export default function BaseLayout() { // 该值可从 localStorage 或 cookie 中读取实现跨会话持久化 const [initialSiderCollapsed, setInitialSiderCollapsed] useState(true); return ( ThemedLayout initialSiderCollapsed{initialSiderCollapsed} Outlet / /ThemedLayout ); }总结ThemedLayout是 Refine 与 Ant Design 集成的核心布局组件它把「侧边栏菜单自动生成」「用户身份展示」「响应式抽屉」「主题联动」等高频能力内置化同时通过Sider、Header、Title、Footer、OffLayoutArea五个 Props 保持高度可定制性。结合initialSiderCollapsed/onSiderCollapsed可轻松实现折叠状态持久化useThemedLayoutContext让你在任意页面掌控折叠行为而swizzle则把完整源码交给开发者自由改造。对于需要快速搭建管理后台、且希望布局风格统一可维护的项目这是一个开箱即用且可深度定制的起点。【免费下载链接】refineA React Framework for building internal tools, admin panels, dashboards B2B apps with unmatched flexibility.项目地址: https://gitcode.com/GitHub_Trending/re/refine创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表