引言
在现代 Web 应用中,一个直观且高效的导航系统至关重要。侧边栏(Sidebar)因其强大的信息承载能力和清晰的视觉层次,成为了桌面端应用的宠儿。然而,当面对手机等小屏幕设备时,固定的侧边栏会占用宝贵的屏幕空间,影响用户体验。
因此,一个优秀的侧边栏设计必须是响应式的:在大屏幕上,它应该常驻在侧边;在小屏幕上,它应该能够收起,并通过一个汉堡菜单(Hamburger Menu)按钮来控制其展开与收起。实现这一目标的核心技术,就是监听浏览器窗口的尺寸变化。
第一步:理解核心概念
在动手编写代码之前,我们需要理解几个关键点:
- 状态管理 (State Management): 我们需要一个状态来记录侧边栏是打开还是关闭。在 React 中,这通常通过
useStateHook 来实现,例如const [isSidebarOpen, setIsSidebarOpen] = useState(false);。 - 屏幕断点 (Screen Breakpoint): 我们需要定义一个临界尺寸,比如 768px。小于这个尺寸的设备被认为是移动端,大于或等于的则认为是桌面端。这与 Tailwind CSS 的
md:前缀(默认断点为 768px)相呼应。 - 监听窗口大小 (Listening to Resize Event): 我们需要使用 JavaScript 的
window.addEventListener('resize', handler)来监听resize事件,每当用户调整浏览器窗口大小时,handler函数就会被触发。 - 动态样式 (Dynamic Styling): 根据侧边栏的打开/关闭状态 (
isSidebarOpen) 和当前屏幕尺寸 (isMobile),我们需要动态地应用不同的 CSS 样式(例如,使用translateX来控制侧边栏的显示与隐藏)。
第二步:监听窗口大小变化
在 React 组件中,我们使用 useEffect Hook 来处理副作用,监听窗口大小变化就是典型的副作用操作。
// 1. 定义状态来标识是否为移动端
const [isMobile, setIsMobile] = useState(false);
useEffect(() => {
// 2. 定义一个检查函数
const checkIsMobile = () => {
// 通过比较 window.innerWidth 与预设断点 (如 768px) 来判断
setIsMobile(window.innerWidth < 768);
};
// 3. 组件挂载后立即执行一次,以获取初始状态
checkIsMobile();
// 4. 定义 resize 事件的处理函数
const handleResize = () => {
// 当窗口大小改变时,重新检查并更新 isMobile 状态
checkIsMobile();
};
// 5. 添加事件监听器
window.addEventListener('resize', handleResize);
// 6. 返回清理函数
// 在组件卸载时移除监听器,防止内存泄漏
return () => {
window.removeEventListener('resize', handleResize);
};
}, []); // 空依赖数组,确保此 effect 只在组件挂载和卸载时运行一次
这段代码是响应式设计的基石。它确保了无论用户何时调整窗口大小,isMobile 这个状态都能实时反映当前的屏幕尺寸,从而为后续的 UI 逻辑提供依据。
第三步:构建侧边栏 UI 与逻辑
有了 isMobile 状态,我们就可以构建一个能够适应不同屏幕的侧边栏了。
A. 结构与基础样式
侧边栏通常是一个固定在页面左侧的 aside 元素。
<aside className={cn(
// 基础样式:固定定位、全屏高、固定宽度、背景色、右侧边框、层级
"fixed top-0 left-0 h-screen w-32 bg-background border-r z-50",
// 添加平滑的变换过渡效果
"transform transition-transform duration-300 ease-in-out",
// 这里是核心逻辑,稍后解释
isSidebarOpen ? "translate-x-0" : "-translate-x-full",
// 桌面端样式:侧边栏始终可见
"md:translate-x-0"
)}>
{/* 侧边栏内容 */}
</aside>
B. 核心响应式逻辑:三元运算符
关键在于 isSidebarOpen ? "translate-x-0" : "-translate-x-full" 这部分。
isSidebarOpen: 这是我们通过useState管理的状态。在移动端,当用户点击汉堡菜单按钮时,我们会调用setIsSidebarOpen(true)或setIsSidebarOpen(false)来切换它。"translate-x-0": 如果isSidebarOpen为true,这个 CSS 类就会被应用。translate-x-0表示元素在 X 轴上没有位移,因此侧边栏会显示在屏幕上。"-translate-x-full": 如果isSidebarOpen为false,这个 CSS 类就会被应用。-translate-x-full表示元素在 X 轴上向左移动其自身的 100% 宽度,这就让它完全滑出了屏幕左侧,实现了隐藏效果。
C. 桌面端逻辑:Tailwind 的 md: 前缀
"md:translate-x-0" 是 Tailwind CSS 的响应式设计语法。它表示:在中等屏幕尺寸(md,默认 >= 768px)及以上时,应用 translate-x-0 样式。
这意味着,在桌面端,无论 isSidebarOpen 的值是什么,md:translate-x-0 都会强制侧边栏保持可见状态,覆盖了前面三元运算符可能产生的隐藏效果。
D. 完整的组合效果
-
在桌面端 (>= 768px):
isMobile为false。md:translate-x-0生效,侧边栏始终显示。isSidebarOpen状态和三元运算符的逻辑在此场景下不影响侧边栏的可见性。
-
在移动端 (< 768px):
-
isMobile为true。 -
md:translate-x-0不生效。 -
侧边栏的显示/隐藏完全由
isSidebarOpen状态和三元运算符控制:- 如果
isSidebarOpen为true,侧边栏显示 (translate-x-0)。 - 如果
isSidebarOpen为false,侧边栏隐藏 (-translate-x-full)。
- 如果
-
第四步:交互逻辑
最后,为了让一切联动起来,我们需要添加交互逻辑。例如,在 handleNav 函数中,当在移动端导航时,应自动关闭侧边栏,以提供更好的体验。
const handleNav = (path: string) => {
// ... 其他导航逻辑 ...
// 如果是移动端且侧边栏打开,则关闭侧边栏
if (isMobile && isSidebarOpen) {
setIsSidebarOpen(false); // 更新状态
}
};
通过这种方式,我们就创建了一个功能完整、响应迅速的侧边栏。它能够智能地根据窗口大小调整自身行为,为不同设备的用户提供最佳的导航体验。