ARTICLE DETAIL

资讯详情

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

refine v3 Chakra UI 布局定制实战:从 LayoutProps 到 ThemedLayout 源码级解析

refine v3 Chakra UI 布局定制实战:从 LayoutProps 到 ThemedLayout 源码级解析 refine v3 Chakra UI 布局定制实战从 LayoutProps 到 ThemedLayout 源码级解析【免费下载链接】refineA React Framework for building internal tools, admin panels, dashboards B2B apps with unmatched flexibility.项目地址: https://gitcode.com/GitHub_Trending/re/refine本文基于 refine 官方文档中 Chakra UI 的 Layout 定制指南documentation/versioned_docs/version-3.xx.xx/api-reference/chakra-ui/customization/layout.md讲解如何在 refine 3.x 版本中使用Refine与LayoutWrapper两个入口实现全局/局部布局定制并结合当前开源仓库的源码深入剖析LayoutProps类型定义、useMenu钩子与默认主题布局ThemedLayout的实际渲染逻辑帮助你在内部工具与管理面板项目中完整掌握自定义侧边栏、顶部导航与整体框架的实现原理。布局定制的两个入口全局与局部在 refine 的 Chakra UI 集成中自定义布局围绕两个组件展开Refine全局定制应用入口组件通过Layout、Sider、Header、Footer、OffLayoutArea、Title等 props 影响整个应用的框架结构LayoutWrapper局部定制在自定义页面custom page中使用时允许对单个页面做局部布局覆盖。可定制的核心组件共六个组件职责Layout整体布局容器决定 Sider / Header / Footer / OffLayoutArea 的组织方式Sider侧边导航栏渲染由resources生成的菜单项Header顶部栏通常包含汉堡菜单与当前用户信息Footer页脚区域可选OffLayoutArea位于主布局区域之外、仍参与整体框架的扩展区域可选Title品牌标题在侧边栏顶部展示需接收collapsed状态全局布局的详细 props 说明可参考Refine配置文档refine-configLayoutWrapper的用法参见 layout-wrapper。另外官方还提供了Swizzle机制通过 refine CLI仓库中对应 packages/cli 目录可以把默认布局组件复制到本地代码库从而以源码方式修改组件而不必整体替换。核心类型LayoutProps 到底能接收什么文档示例中用到的LayoutProps类型在源码中的权威定义位于 packages/core/src/contexts/refine/types.tsexport type TitleProps { collapsed: boolean; }; export type LayoutProps { Sider?: React.FC{ Title?: React.FCTitleProps; render?: (props: { /** 由 Refine 中 resources 生成的菜单项 */ items: React.JSX.Element[]; /** 定义了 authProvider 且会话已认证时的 logout 按钮 */ logout: React.ReactNode; /** 侧边栏是否处于折叠态 */ collapsed: boolean; }) React.ReactNode; meta?: Recordstring, unknown; }; Header?: React.FC; Title?: React.FCTitleProps; Footer?: React.FC; OffLayoutArea?: React.FC; children?: ReactNode; };从类型定义可以看出几个关键设计Sider的renderprop 是半定制入口它把框架已渲染好的items菜单节点数组、logout按钮和collapsed状态交给你你只负责组合与排版无需自己处理菜单生成逻辑Title必须感知collapsed状态折叠态下通常只显示图标展开态显示图标 文字children即页面内容插槽自定义Layout组件最终负责渲染它。实战创建一个顶部导航式 CustomLayout下面完整继承官方文档中的示例不使用默认的“侧边栏 顶栏”框架而是用一个顶部水平导航条加内容区的全自定义布局替换。import { Refine, LayoutProps, useMenu, useRouterContext, } from pankod/refine-core; import routerProvider from pankod/refine-react-router-v6; import dataProvider from pankod/refine-simple-rest; import { ChakraProvider, ErrorComponent, ReadyPage, useNotificationProvider, refineTheme, Box, HStack, Button, } from pankod/refine-chakra-ui; import { PostCreate, PostEdit, PostList } from ./pages; // 自定义布局组件接收 LayoutProps渲染顶部菜单 页面内容 const CustomLayout: React.FCLayoutProps ({ children }) { const { menuItems, selectedKey } useMenu(); const { Link } useRouterContext(); return ( Box displayflex flexDirectioncolumn {/* 顶部水平导航条 */} Box pt2 px4 bgchakra-body-bg borderBottom1px borderColorgray.200 HStack {menuItems.map(({ route, label, icon }) ( Box key{route} Button as{Link} to{route} label{label} variantghost colorSchemegreen leftIcon{icon ?? ((IconList size{20} /) as any)} isActive{route selectedKey} borderBottomLeftRadius0 borderBottomRightRadius0 {label} /Button /Box ))} /HStack /Box {/* 页面内容区 */} Box{children}/Box /Box ); }; const App () { return ( ChakraProvider theme{refineTheme} Refine routerProvider{routerProvider} dataProvider{dataProvider(https://api.fake-rest.refine.dev)} notificationProvider{notificationProvider()} ReadyPage{ReadyPage} Layout{CustomLayout} resources{[ { name: posts, list: PostList }, { name: categories, list: DummyListPage, icon: IconCategory / }, { name: users, list: DummyListPage, icon: IconUsers / }, ]} / /ChakraProvider ); };这段代码中有三个值得展开的技术点1.useMenu菜单数据从哪来示例中用useMenu拿到当前资源的菜单列表并渲染为导航按钮。该钩子实现于 packages/core/src/hooks/menu/useMenu.tsx其返回结构为type UseMenuReturnType { defaultOpenKeys: string[]; // 当前选中项的祖先资源 key用于展开树形菜单 selectedKey: string; // 当前路由对应的菜单 key menuItems: TreeMenuItem[]; // 由 resources 生成的树形菜单项 };TreeMenuItem携带route、label、icon、children等字段——这正是示例中menuItems.map(({ route, label, icon }) ...)解构的依据。菜单项由Refine的resourcesprop 派生并经过useGetToPath生成路由、useUserFriendlyName生成友好名称它还支持meta参数透传给自定义菜单实现UseMenuProps见 useMenu.tsx因此你在resources中定义的icon字段可以直接出现在自定义布局中无需硬编码图标。2.useRouterContext的Link跨路由库的链接抽象示例里Button as{Link} to{route}的写法是为了不直接依赖 react-router 的具体组件。useRouterContext().Link是 refine 对路由层的统一抽象——换用其他 routerProvider如 nextjs-router时布局代码无需改动。3.refineTheme与 ChakraProviderChakra UI 集成要求用ChakraProvider theme{refineTheme}包裹应用。refineTheme中注册了refine.sider.bg.light/dark、refine.header.bg.light/dark等语义色 token默认布局组件大量使用useColorModeValue引用这些 token后文源码分析可见自定义布局沿用同一主题体系才能保证亮/暗模式一致。文档也提示该示例演示的是全局布局配置若需要在自定义页面中局部修改布局应转向LayoutWrapper的用法。默认布局的源码级实现ThemedLayout当不传Layout时refinedev/chakra-ui3.x 版本为pankod/refine-chakra-ui使用默认的ThemedLayout组件其实现位于 packages/chakra-ui/src/components/themedLayout/index.tsxexport const ThemedLayout: React.FCRefineThemedLayoutProps ({ Sider, Header, Title, Footer, OffLayoutArea, children, initialSiderCollapsed, onSiderCollapsed, }) { const SiderToRender Sider ?? DefaultSider; const HeaderToRender Header ?? DefaultHeader; return ( ThemedLayoutContextProvider initialSiderCollapsed{initialSiderCollapsed} onSiderCollapsed{onSiderCollapsed} Box displayflex SiderToRender Title{Title} / Box displayflex flexDirectioncolumn flex{1} minH100vh overflowclip HeaderToRender / Box p{[2, 4]}{children}/Box {Footer Footer /} /Box {OffLayoutArea OffLayoutArea /} /Box /ThemedLayoutContextProvider ); };从源码结构可以看出默认框架的组织方式Sider 固定在最左右侧是「Header 内容区 Footer」的纵向 flex 容器OffLayoutArea 追加在最外层右侧。同时Sider与Header都有默认实现兜底Sider ?? DefaultSider这意味着你可以只替换其中一个部件而保留另一个的默认行为——这正是LayoutProps中各组件均为可选的设计意图。与核心LayoutProps相比Chakra UI 的布局类型在 packages/ui-types/src/types/layout.tsx 中做了扩展export type RefineThemedLayoutProps { initialSiderCollapsed?: boolean; // 侧边栏默认是否折叠 onSiderCollapsed?: (collapsed: boolean) void; // 折叠状态变化回调 } RefineLayoutLayoutProps; export type RefineThemedLayoutSiderProps RefineLayoutSiderProps { activeItemDisabled?: boolean; // 选中项是否禁止点击如不可返回的列表页 siderItemsAreCollapsed?: boolean; // 菜单项是否默认收起子菜单 }; export type RefineThemedLayoutHeaderProps RefineLayoutHeaderProps { sticky?: boolean; // Header 是否吸顶 };其中initialSiderCollapsed/onSiderCollapsed通过ThemedLayoutContextProvider注入上下文供 Sider 内部的汉堡菜单读写折叠状态而不用在组件间层层透传 props。Sider 与 Header 的内部行为ThemedSider响应式、权限感知与未保存变更保护默认侧边栏实现见 packages/chakra-ui/src/components/themedLayout/sider/index.tsx几个与定制决策直接相关的行为响应式策略桌面端[none,none,flex]断点渲染固定定位侧边栏宽度在展开 200px 与折叠 56px 之间带 200ms 过渡移动端则以 ChakraDrawer形式从左侧滑出sider/index.tsx折叠态 TooltipcommonTooltipProps在siderCollapsed为真且移动端抽屉未打开时启用折叠图标悬停可看到完整 label权限过滤每个菜单项都包裹在CanAccess resource{name} actionlist中sider/index.tsx配合accessControlProvider时不可访问的资源菜单项不会渲染——自定义 Sider 若不自己做这层过滤可能出现“菜单可见但页面 403”的不一致登出安全handleLogout会先检查useWarnAboutChange的warnWhen状态存在未保存表单变更时弹出 confirm确认后才调用mutateLogout()sider/index.tsx。如果你用renderprop 重组 Sider应复用框架提供的logout节点而非自己拼登出按钮以保留该保护逻辑菜单树渲染renderTreeView递归处理children父级菜单用Accordion展开/收起默认展开项由useMenu返回的defaultOpenKeys决定。ThemedHeader用户身份展示与吸顶默认顶栏实现见 packages/chakra-ui/src/components/themedLayout/header/index.tsx通过useGetIdentity()读取当前用户渲染用户名与头像user.name、user.avatar未提供authProvider时自动为空无需条件判断支持stickyprop为真时应用position: sticky; top: 0; zIndex: 1让 Header 在长列表页滚动时保持可见背景色取useColorModeValue(refine.header.bg.light, refine.header.bg.dark)与主题 token 联动。顶栏左侧的HamburgerMenuhamburgerMenu则是切换 Sider 折叠态的触发器依赖前面提到的ThemedLayoutContext。集成测试如何验证布局默认布局并非“写了就算数”packages/chakra-ui/src/components/themedLayout/index.spec.tsx 将ThemedLayout绑定到共享用例集layoutLayoutTests来自 packages/ui-tests执行断言。从测试结构看各 UI 集成包共用同一套布局行为契约这解释了为什么 Chakra / Ant Design / MUI 各包的 Sider 行为折叠、logout、菜单项高度一致。Swizzle把默认布局复制进本地代码库除了整体替换或传renderprop 的“外部定制”refine 还推荐 Swizzle 工作流使用 refine CLI 将指定 UI 组件如ThemedLayout及其 Sider/Header的源码复制到项目内仓库中 CLI 位于 packages/cli随后在本地文件上自由修改。文档中的 info-tip 明确标注该组件支持 Swizzle原文档 frontmatter 中swizzle: true。三种定制手段的适用场景可以归纳为手段侵入程度适用场景Refine Layout{CustomLayout}整体替换高布局结构完全不同如本例的顶部导航Sider/Header/Footer等单部件替换或renderprop中只改某个部件保留其余默认行为Swizzle 后本地修改源码低耦合、高自由需要改交互细节动画、断点、Tooltip 行为等版本适用性说明本文所有 API 与包名均对应仓库中version-3.xx.xx版本线文档原文档使用的是pankod/refine-core、pankod/refine-chakra-ui、pankod/refine-react-router-v6、pankod/refine-simple-rest等 3.x 包名。需要提醒当前仓库主干代码中的 UI 包已迁移至refinedev/*命名体系pankod/*到refinedev/*的迁移有专门的 codemod 工具packages/codemod若你在新建项目请以最新主文档documentation/docs/api-reference为准从主干源码结构看Layout/Sider等布局 props 的类型契约仍由 LayoutProps 定义并被 UI 包复用Refine的上下文装配逻辑资源、路由、通知等 Provider 层级可参考 packages/core/src/components/containers/refine/index.tsx因此本文讲的useMenu/LayoutProps/ 部件替换思路在各版本间基本一致但具体 prop 挂载位置Refine还是LayoutWrapper建议以对应版本文档为准。小结refine 的布局定制遵循“一个契约、两级入口”LayoutProps定义部件接口packages/core/src/contexts/refine/types.tsRefine做全局替换、LayoutWrapper做页面级覆盖自定义布局的核心数据源是useMenu菜单项 选中态 默认展开键与useRouterContext路由抽象Link二者都与具体路由库和路由库无关想理解默认行为再决定改什么应直接阅读 ThemedLayout、ThemedSider、ThemedHeader 三个文件其中权限过滤CanAccess、未保存变更保护warnWhen与移动端 Drawer 行为是自定义实现时最容易遗漏的等价逻辑修改深度不够时用 Swizzlerefine CLI把组件源码拉到项目内再做行级修改是官方推荐的渐进路径。【免费下载链接】refineA React Framework for building internal tools, admin panels, dashboards B2B apps with unmatched flexibility.项目地址: https://gitcode.com/GitHub_Trending/re/refine创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表