构建响应式侧边栏:从监听窗口大小开始

0 阅读5分钟

引言

在现代 Web 应用中,一个直观且高效的导航系统至关重要。侧边栏(Sidebar)因其强大的信息承载能力和清晰的视觉层次,成为了桌面端应用的宠儿。然而,当面对手机等小屏幕设备时,固定的侧边栏会占用宝贵的屏幕空间,影响用户体验。

因此,一个优秀的侧边栏设计必须是响应式的:在大屏幕上,它应该常驻在侧边;在小屏幕上,它应该能够收起,并通过一个汉堡菜单(Hamburger Menu)按钮来控制其展开与收起。实现这一目标的核心技术,就是监听浏览器窗口的尺寸变化


第一步:理解核心概念

在动手编写代码之前,我们需要理解几个关键点:

  1. 状态管理 (State Management): 我们需要一个状态来记录侧边栏是打开还是关闭。在 React 中,这通常通过 useState Hook 来实现,例如 const [isSidebarOpen, setIsSidebarOpen] = useState(false);
  2. 屏幕断点 (Screen Breakpoint): 我们需要定义一个临界尺寸,比如 768px。小于这个尺寸的设备被认为是移动端,大于或等于的则认为是桌面端。这与 Tailwind CSS 的 md: 前缀(默认断点为 768px)相呼应。
  3. 监听窗口大小 (Listening to Resize Event): 我们需要使用 JavaScript 的 window.addEventListener('resize', handler) 来监听 resize 事件,每当用户调整浏览器窗口大小时,handler 函数就会被触发。
  4. 动态样式 (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" : 如果 isSidebarOpentrue,这个 CSS 类就会被应用。translate-x-0 表示元素在 X 轴上没有位移,因此侧边栏会显示在屏幕上。
  • "-translate-x-full" : 如果 isSidebarOpenfalse,这个 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):

    • isMobilefalse
    • md:translate-x-0 生效,侧边栏始终显示。
    • isSidebarOpen 状态和三元运算符的逻辑在此场景下不影响侧边栏的可见性。
  • 在移动端 (< 768px):

    • isMobiletrue

    • md:translate-x-0 不生效

    • 侧边栏的显示/隐藏完全由 isSidebarOpen 状态和三元运算符控制:

      • 如果 isSidebarOpentrue,侧边栏显示 (translate-x-0)。
      • 如果 isSidebarOpenfalse,侧边栏隐藏 (-translate-x-full)。

第四步:交互逻辑

最后,为了让一切联动起来,我们需要添加交互逻辑。例如,在 handleNav 函数中,当在移动端导航时,应自动关闭侧边栏,以提供更好的体验。

const handleNav = (path: string) => {
    // ... 其他导航逻辑 ...

    // 如果是移动端且侧边栏打开,则关闭侧边栏
    if (isMobile && isSidebarOpen) {
        setIsSidebarOpen(false); // 更新状态
    }
};

通过这种方式,我们就创建了一个功能完整、响应迅速的侧边栏。它能够智能地根据窗口大小调整自身行为,为不同设备的用户提供最佳的导航体验。