一、Resizable 的前世今生
react-resizable-panels 是高性能面板布局库,由 React 核心开发者 bvaughn 打造,用于创建可拖拽调整的多面板界面。
shadcn-ui 的 Resizable 组件 基于前者封装,整合 Tailwind CSS 样式,通过 npx shadcn-ui@latest add resizable 一键安装,无需额外配置。
二、核心差异对比
| 特性 | react-resizable-panels | shadcn-ui Resizable |
|---|---|---|
| 组件命名 | ResizablePanels(容器) | Resizable(容器) |
ResizablePanel(面板) | ResizablePanel(面板) | |
PanelDivider(分隔条) | ResizableDivider(分隔条) | |
| 布局方向 | direction="horizontal/vertical" | orientation="horizontal/vertical" |
| 事件监听 | onChange | onSizesChange |
| 样式集成 | 需手动编写 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. 拖拽交互逻辑
- 分隔条监听鼠标按下(
onMouseDown),记录初始位置。 - 全局监听鼠标移动(
onMouseMove),计算位移并动态调整面板尺寸。 - 鼠标释放时停止监听(
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} |
六、常见问题与解决方案
-
拖拽不流畅
- 原因:频繁触发重排(reflow)。
- 方案:避免在拖拽回调中执行复杂计算,使用
requestAnimationFrame优化。
-
嵌套布局高度异常
- 原因:内层容器高度未明确指定。
- 方案:为内层
Resizable添加style={{ height: '100%' }}。
-
样式定制
- 通过
className覆盖默认样式:<ResizableDivider className="bg-primary-500 w-2" />
- 通过
总结
shadcn-ui 的 Resizable 组件通过封装底层库,提供了简单易用、美观且高性能的面板布局方案。核心优势:
- 零配置:一键安装,自动集成样式系统。
- 响应式:完美支持 Tailwind 的断点系统。
- 可扩展:通过自定义组件和事件,满足复杂场景需求。
如需深入了解,可参考 react-resizable-panels 官方文档。