shadcn-ui 组件学习 —— Resizable

243 阅读3分钟

一、Resizable 的前世今生

react-resizable-panels 是高性能面板布局库,由 React 核心开发者 bvaughn 打造,用于创建可拖拽调整的多面板界面。
shadcn-ui 的 Resizable 组件 基于前者封装,整合 Tailwind CSS 样式,通过 npx shadcn-ui@latest add resizable 一键安装,无需额外配置。

二、核心差异对比

特性react-resizable-panelsshadcn-ui Resizable
组件命名ResizablePanels(容器)Resizable(容器)
ResizablePanel(面板)ResizablePanel(面板)
PanelDivider(分隔条)ResizableDivider(分隔条)
布局方向direction="horizontal/vertical"orientation="horizontal/vertical"
事件监听onChangeonSizesChange
样式集成需手动编写 CSS内置 Tailwind 样式,支持主题配置

三、基础用法

1. 水平双面板布局
<Resizable orientation="horizontal">
  <ResizablePanel minSize={200} defaultSize={300}>
    左侧面板(最小200px,初始300px)
  </ResizablePanel>
  <ResizableDivider />
  <ResizablePanel minSize={200} defaultSize="70%">
    右侧面板(最小200px,初始占70%宽度)
  </ResizablePanel>
</Resizable>
2. 嵌套复杂布局
<Resizable orientation="horizontal">
  <ResizablePanel defaultSize={300}>
    左侧固定面板
  </ResizablePanel>
  <ResizableDivider />
  <ResizablePanel defaultSize="70%">
    {/* 右侧嵌套垂直布局 */}
    <Resizable orientation="vertical">
      <ResizablePanel defaultSize="60%">顶部子面板</ResizablePanel>
      <ResizableDivider />
      <ResizablePanel defaultSize="40%">底部子面板</ResizablePanel>
    </Resizable>
  </ResizablePanel>
</Resizable>
3. 状态持久化(保存面板尺寸)
function App() {
  const [sizes, setSizes] = useState([300, '70%']);
  
  return (
    <Resizable 
      orientation="horizontal"
      onSizesChange={setSizes} // 自动更新尺寸状态
    >
      <ResizablePanel size={sizes[0]} />
      <ResizableDivider />
      <ResizablePanel size={sizes[1]} />
    </Resizable>
  );
}

四、实现原理(简化版)

1. 技术底层
  • 核心依赖:基于 react-resizable-panels 实现布局逻辑,使用 CSS Grid/Flexbox 渲染。
  • 样式系统:通过 Tailwind CSS 定义默认样式,支持自定义主题。
2. 拖拽交互逻辑
  1. 分隔条监听鼠标按下onMouseDown),记录初始位置。
  2. 全局监听鼠标移动onMouseMove),计算位移并动态调整面板尺寸。
  3. 鼠标释放时停止监听onMouseUp),完成拖拽。
sequenceDiagram
  participant 用户
  participant 分隔条
  participant 面板
  用户->>分隔条: 鼠标按下(开始拖拽)
  分隔条->>面板: 记录初始位置
  loop 鼠标移动
    用户->>分隔条: 鼠标移动
    分隔条->>面板: 更新尺寸(CSS Grid/Flex)
  end
  用户->>分隔条: 鼠标释放(结束拖拽)
3. 嵌套布局原理
  • 父子容器关系:每个 Resizable 是独立容器,内层容器尺寸受限于外层面板。
  • 递归渲染:通过嵌套 Resizable 组件实现复杂网格,每层独立管理拖拽逻辑。

五、关键属性速查表

容器属性说明示例
orientation布局方向(水平/垂直)orientation="horizontal"
onSizesChange尺寸变化回调onSizesChange={(sizes) => ...}
proportionalResize按比例调整(非固定像素)proportionalResize
面板属性说明示例
defaultSize初始尺寸(像素或百分比)defaultSize={300}"50%"
minSize/maxSize最小/最大尺寸限制minSize={200}
size受控模式下的当前尺寸size={panelSize}

六、常见问题与解决方案

  1. 拖拽不流畅

    • 原因:频繁触发重排(reflow)。
    • 方案:避免在拖拽回调中执行复杂计算,使用 requestAnimationFrame 优化。
  2. 嵌套布局高度异常

    • 原因:内层容器高度未明确指定。
    • 方案:为内层 Resizable 添加 style={{ height: '100%' }}
  3. 样式定制

    • 通过 className 覆盖默认样式:
      <ResizableDivider className="bg-primary-500 w-2" />
      

总结

shadcn-ui 的 Resizable 组件通过封装底层库,提供了简单易用、美观且高性能的面板布局方案。核心优势:

  • 零配置:一键安装,自动集成样式系统。
  • 响应式:完美支持 Tailwind 的断点系统。
  • 可扩展:通过自定义组件和事件,满足复杂场景需求。

如需深入了解,可参考 react-resizable-panels 官方文档