react-grid-layout是 React 生态中一个非常流行的、用于构建可拖拽、可调整大小、响应式网格布局的库。它的强大之处在于用简洁的 API 实现了复杂的布局管理。
一、 布局、坐标
react-grid-layout的实现基石在于它将组件的实际屏幕位置与抽象的网格位置彻底分离
1. 布局数组
不直接存储组件的像素位置,而是维护一个名为 layout 的 JavaScript 对象数组
每个元素(item)在布局数组中都是一个对象,包含以下关键属性:
| 属性 | 类型 | 描述 |
|---|---|---|
i | String | 元素的唯一 ID,对应于其 key 或子组件的 key。 |
x | Number | 元素在网格中的列起始坐标(Grid X)。 |
y | Number | 元素在网格中的行起始坐标(Grid Y)。 |
w | Number | 元素的宽度,占用的网格列数。 |
h | Number | 元素的高度,占用的网格行数。 |
static | Boolean | 如果为 true,则元素不可拖拽和调整大小。 |
2. 坐标转换
核心逻辑在于将上述抽象的 (x, y, w, h) 网格坐标实时转换成浏览器能理解的 CSS 像素坐标
该转换依赖于两个配置项:
cols: 网格的总列数rowHeight: 每行网格的高度(像素值)margin: 网格项之间的间隔(像素值)
利用这些公式计算每个网格项的 top、left、width 和 height,并通过 CSS transform: translate(x, y) 来定位组件,而不是传统的 top/left,这能带来更好的性能。
二、 核心组件结构
主要由以下三个 React 组件构成:
1. ResponsiveReactGridLayout
这是最外层的容器组件。它负责处理响应式逻辑
- 监听窗口大小变化(
resize事件) - 根据当前的容器宽度,确定应该加载哪个断点(Breakpoint) (例如:
lg,md,sm等) - 根据断点和其对应的
layout配置,渲染ReactGridLayout
2. ReactGridLayout
这是网格渲染的核心组件,它负责:
- 计算和设置容器的总高度(
min-height),以确保所有网格项都能被容纳。 - 将
layout数组遍历,为每一个网格项渲染一个GridItem。 - 管理拖拽和调整大小操作的状态(影子/占位符)。
3. GridItem
这是每个可拖拽/调整大小的网格项的容器
- 渲染一个内部的
div来包裹用户传入的子组件 - 注入拖拽句柄和调整大小句柄 。
- 通过监听鼠标事件(
onMouseDown/onMouseMove/onMouseUp)实现交互
三、 拖拽和调整大小原理
拖拽和调整大小依赖于两个库react-draggable与react-resizable
1. 占位符与状态管理
当用户开始拖拽或调整大小时,不会立即更新 layout 状态,而是通过一种“影子”机制来优化性能和用户体验
- 占位符(Placeholder) : 一个半透明的、与当前操作网格项具有相同尺寸的元素会出现在其下方,指示操作完成后网格项将占据的位置
- 操作过程: 在
onMouseMove过程中,RGL 实时计算鼠标位置对应的新网格坐标 ,并更新占位符的位置 - 操作结束: 只有在
onMouseUp释放时,RGL 才会调用onLayoutChange,将最终的网格坐标更新到父组件中
2. 网格冲突解决算法
- 冲突检测: 检测新位置 是否与任何其他网格项 发生矩形重叠。
- 向下推 : 如果发生冲突,会尝试将 以及与 冲突的其他网格项向下(增大 坐标)推动,直到不再发生冲突
- 紧凑化 : 在每次布局变化后,可以执行 Compaction 算法。它会尝试将所有非静态的网格项向上(减小 坐标)或向左(减小 坐标)移动到可用的最高/最左位置,从而消除网格中的不必要空白