- 前端
- UI组件
【免费下载链接】rsuite
🧱 A suite of React components .
本文以 rsuite 文档中 Sidenav footer 示例 为骨架,讲解如何用Sidenav、Nav、Sidenav.Toggle以及HStack/VStack/InputGroup等布局组件,构建一个带品牌区、搜索框与折叠开关的完整侧边导航栏。读完本文,你将掌握 Sidenav 三段式结构(Header / Body / Footer)的组装方式、expanded受控状态的切换原理,以及折叠态下组件如何联动响应,并能在自己的 React 项目中直接复刻这套代码。
一、示例目标:一个可折叠的完整侧边栏
docs/pages/components/sidenav/fragments/footer.md给出的示例,是一个典型的后台管理系统侧边栏:顶部是品牌 Logo 与搜索框,中间是五个带图标的导航菜单项,底部是展开/折叠切换按钮。整体结构如下:
<Box w={240}> <Sidenav expanded={expanded}> <Sidenav.Header> <Header expanded={expanded} /> </Sidenav.Header> <Sidenav.Body> <Nav> <Nav.Item icon={<DashboardIcon />}>Overview</Nav.Item> {/* ... 其余菜单项 */} </Nav> </Sidenav.Body> <Sidenav.Footer> <Sidenav.Toggle onToggle={setExpanded} /> </Sidenav.Footer> </Sidenav> </Box>在这个示例中,Box w={240}为侧边栏设置了固定宽度容器;expanded是一个React.useState(true)管理的受控状态,由Sidenav.Toggle的onToggle回调驱动更新。从 src/Sidenav/Sidenav.tsx 源码可以看到,Sidenav的expanded属性默认值为true,并通过Transition(timeout={300})为宽度变化提供 300ms 的折叠/展开过渡动画。
二、三段式结构:Header / Body / Footer 的职责划分
Sidenav组件通过静态子组件(Subcomponents)挂载了Header、Body、Footer、GroupLabel、Toggle五个子组件(见 src/Sidenav/Sidenav.tsx)。其中Header、Body、Footer本身是纯粹的结构化容器,由createComponent生成为普通div元素(见 SidenavHeader.tsx、SidenavBody.tsx、SidenavFooter.tsx),真正的行为逻辑由Nav与Sidenav.Toggle承载。
1. Header:品牌区与搜索框的折叠联动
示例中的Header是一个自定义组件,根据expanded状态切换渲染内容:
const Header = ({ expanded }) => { if (!expanded) { return ( <HStack justifyContent="center"> <SiProtondb size={32} /> </HStack> ); } return ( <VStack p="10px 10px 0 10px" spacing={12}> <HStack> <SiProtondb size={32} /> Brand </HStack> <InputGroup inside size="sm"> <InputGroup.Addon> <SearchIcon /> </InputGroup.Addon> <Input type="search" placeholder="Search here..." /> </InputGroup> </VStack> ); };- 折叠态(
expanded === false):只居中显示品牌图标,避免在窄条侧边栏中撑开宽度。 - 展开态:使用
VStack(垂直排列)组合「品牌名 + 搜索框」,spacing={12}控制垂直间距;InputGroup inside实现图标内嵌的搜索输入框,配合@rsuite/icons/Search图标。 - 搜索框在折叠时被移除,这是侧边栏折叠设计的常见做法,因为 52px 宽的窄条无法容纳输入控件。
对应样式上,src/Sidenav/styles/index.scss中.rs-sidenav-header通过padding: var(--rs-sidenav-header-p)控制内边距,而折叠态(.rs-sidenav-collapse-out)会隐藏所有.rs-sidenav-item-title文本,仅保留居中的图标。
2. Body:菜单导航的核心区域
Sidenav.Body内部渲染Nav组件及五个Nav.Item,每个菜单项都通过icon属性挂载@rsuite/icons图标:
<Nav> <Nav.Item icon={<DashboardIcon />}>Overview</Nav.Item> <Nav.Item icon={<PeoplesIcon />}>Customers</Nav.Item> <Nav.Item icon={<PieChartIcon />}>Analytics</Nav.Item> <Nav.Item icon={<DataAuthorizeIcon />}>Security</Nav.Item> <Nav.Item icon={<SettingIcon />}>Settings</Nav.Item> </Nav>从样式源码看,.rs-sidenav-body采用flex: 1 1 auto; overflow: auto,保证菜单区域在侧边栏中自动撑满剩余高度,并在内容超长时可滚动;折叠时(.rs-sidenav-collapse-out .rs-sidenav-body)则改为overflow: inherit,让下拉子菜单可以溢出显示。折叠态下,.rs-sidenav-item会通过justify-content: center与height: var(--rs-sidenav-item-height)(36px)实现图标垂直居中的窄条样式,并隐藏文字标题。
3. Footer:折叠开关的挂载位置
Sidenav.Footer在样式上拥有自己的职责:src/Sidenav/styles/index.scss中.rs-sidenav-footer设置了border-top: 1px solid(上边框)、margin-top: auto(推到容器底部)以及padding: var(--rs-sidenav-footer-p)。示例将Sidenav.Toggle放在此处,视觉上形成「菜单列表 → 分隔线 → 折叠按钮」的经典底栏布局。
三、Sidenav.Toggle:折叠开关的工作原理
Sidenav.Toggle是本示例的核心交互元素,其实现位于 src/Sidenav/SidenavToggle.tsx:
const sidenav = useContext(SidenavContext); if (!sidenav) { console.error('<Sidenav.Toggle> must be rendered within a <Sidenav>'); return null; } const expanded = sidenav.expanded; const handleToggle = useEventCallback((event) => { onToggle?.(!expanded, event); onClick?.(event); }); return ( <IconButton icon={<ArrowLeftLineIcon aria-label="" />} onClick={handleToggle} aria-label={expanded ? 'Collapse' : 'Expand'} {...rest} /> );几个关键实现细节:
- 依赖 Sidenav Context:
Sidenav.Toggle通过useContext(SidenavContext)读取expanded状态。若被渲染在Sidenav之外,会输出错误信息并返回null。SidenavContext在 src/Sidenav/SidenavContext.tsx 中定义,由Sidenav在渲染时通过SidenavContext.Provider注入expanded、openKeys、onOpenChange等值。 - 受控回调:点击按钮时调用
onToggle(!expanded, event)。示例中的onToggle={setExpanded}直接把新的展开状态写回 React state,实现真正的受控折叠。 - 无障碍支持:按钮的
aria-label随状态切换为'Collapse'或'Expand',折叠时图标(ArrowLeftLineIcon)旋转 180°(样式见.rs-sidenav-toggle-collapsed .rs-icon)。 - 图标动画:
.rs-sidenav-toggle的图标transition: transform 0.3s ease,折叠/展开时箭头平滑旋转。
在 SidenavToggle 测试 中验证了这些行为:展开态渲染Collapse按钮、折叠态渲染Expand按钮、点击触发onToggle(false, event),以及脱离Sidenav渲染时抛出错误。
四、样式与动画:折叠如何“动”起来
侧边栏的宽度切换并非瞬间完成,而是由 src/Sidenav/Sidenav.tsx 中的Transition组件驱动:根据expanded状态,在展开(collapse-in)、折叠(collapse-out)与过渡(collapsing)三种 className 间切换。
对应的样式定义在 src/Sidenav/styles/index.scss:
| CSS 变量 | 默认值 | 作用 |
|---|---|---|
--rs-sidenav-width | 52px | 折叠态侧边栏宽度 |
--rs-sidenav-collapse-in-width | 100% | 展开态宽度(撑满容器) |
--rs-sidenav-collapse-transition | 0.15s ease-in | 宽度过渡时长与缓动 |
--rs-sidenav-item-height | 36px | 折叠态菜单项高度 |
--rs-sidenav-header-p/--rs-sidenav-footer-p | 派生自--rs-sidenav-p | Header / Footer 内边距 |
- 展开态
.rs-sidenav-collapse-in将宽度设为100%,菜单文字、搜索框等全部可见; - 折叠态
.rs-sidenav-collapse-out将宽度收缩到52px,文字通过sideNavFoldedText关键帧动画淡出(max-width200px → 0,透明度 0.8 → 0); - 折叠后的下拉菜单(
.rs-dropdown-menu)会脱离窄条,以浮层形式从inset-inline-start: 28px位置展开——这正是示例中折叠后仅剩图标、点击可弹出子菜单的基础。
需要说明的是,示例中的Box w={240}是侧边栏展开时的容器宽度;实际项目中也可以让Sidenav直接占据布局宽度,并通过expanded切换实现「宽 240px ⇆ 窄 52px」的布局自适应。
五、完整可运行示例
将示例中的ReactDOM.render替换为现代 React 的createRoot后,完整代码可直接运行(需安装rsuite、@rsuite/icons、react-icons):
import { useState } from 'react'; import DashboardIcon from '@rsuite/icons/Dashboard'; import PeoplesIcon from '@rsuite/icons/Peoples'; import SettingIcon from '@rsuite/icons/Setting'; import PieChartIcon from '@rsuite/icons/PieChart'; import DataAuthorizeIcon from '@rsuite/icons/DataAuthorize'; import SearchIcon from '@rsuite/icons/Search'; import { Sidenav, Nav, HStack, VStack, Input, InputGroup, Box } from 'rsuite'; import { SiProtondb } from 'react-icons/si'; const Header = ({ expanded }) => { if (!expanded) { return ( <HStack justifyContent="center"> <SiProtondb size={32} /> </HStack> ); } return ( <VStack p="10px 10px 0 10px" spacing={12}> <HStack> <SiProtondb size={32} /> Brand </HStack> <InputGroup inside size="sm"> <InputGroup.Addon> <SearchIcon /> </InputGroup.Addon> <Input type="search" placeholder="Search here..." /> </InputGroup> </VStack> ); }; const App = () => { const [expanded, setExpanded] = useState(true); return ( <Box w={240}> <Sidenav expanded={expanded}> <Sidenav.Header> <Header expanded={expanded} /> </Sidenav.Header> <Sidenav.Body> <Nav> <Nav.Item icon={<DashboardIcon />}>Overview</Nav.Item> <Nav.Item icon={<PeoplesIcon />}>Customers</Nav.Item> <Nav.Item icon={<PieChartIcon />}>Analytics</Nav.Item> <Nav.Item icon={<DataAuthorizeIcon />}>Security</Nav.Item> <Nav.Item icon={<SettingIcon />}>Settings</Nav.Item> </Nav> </Sidenav.Body> <Sidenav.Footer> <Sidenav.Toggle onToggle={setExpanded} /> </Sidenav.Footer> </Sidenav> </Box> ); }; export default App;六、进阶:折叠态下的菜单与状态管理
Sidenav不止于视觉折叠,还通过openKeys机制管理多级菜单的展开状态:
defaultOpenKeys:非受控模式下默认展开的菜单eventKey数组;openKeys+onOpenChange:受控模式下由外部接管菜单开合;- 折叠态下点击带有子菜单的菜单项时,会以浮层 + Tooltip 形式展示子菜单——这在 Sidenav 测试 中有明确验证(
mouseOver弹出tooltip,点击后关闭)。
此外,appearance属性支持'default' | 'inverse' | 'subtle'三种风格,分别对应 styles/index.scss 中的默认浅色、深色反白与透明背景三套样式,均通过data-appearance属性区分。activeKey与onSelect已标记为@deprecated,官方建议改用<Nav activeKey>与<Nav onSelect>(见 src/Sidenav/Sidenav.tsx),示例代码中正是采用「在Sidenav.Body内放Nav」的推荐写法,路由切换时只需给Nav传入activeKey即可高亮当前菜单。
七、总结
footer.md示例演示了 rsuiteSidenav组件的完整三段式组装:Sidenav.Header承载品牌与搜索(随expanded联动显隐),Sidenav.Body承载Nav菜单,Sidenav.Footer挂载Sidenav.Toggle完成折叠开关。其背后是SidenavContext对expanded的共享注入、Transition组件的 300ms 动画驱动,以及 CSS 变量与关键帧动画对窄条折叠态的样式支撑。掌握这套模式后,你可以轻松在管理后台、数据看板等场景中快速落地一个可折叠、可无障碍操作的侧边导航。
相关源码索引:Sidenav 主组件、Sidenav.Toggle、SidenavContext、Sidenav 样式、Sidenav 测试
- 前端
- UI组件
【免费下载链接】rsuite
🧱 A suite of React components .
相关推荐
rsuite Sidenav 受控展开与折叠:从 Toggle 到源码级实现解析
rsuite Sidenav 受控展开与折叠:从 Toggle 到源码级实现解析 Sidenav(侧边导航栏)是 rsuite 中用于页面侧边栏导航的核心组件,
前端UI组件RSuite Sidenav 组件完整指南:构建可折叠、可定制、多级分组的页面侧边栏导航
RSuite Sidenav 组件完整指南:构建可折叠、可定制、多级分组的页面侧边栏导航 Sidenav 是 RSuite 中对页面侧边栏场景下的 Nav 导航
前端UI组件RSuite Navbar 导航栏组件基础用法:从 Brand 到响应式布局的完整实战
RSuite Navbar 导航栏组件基础用法:从 Brand 到响应式布局的完整实战 导读 本文以 RSuite 官方文档中 Navbar 组件的基础示例(
前端UI组件
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考