React-Grid-Layout(译文) 中文文档

10,592 阅读9分钟

React-Grid-Layout(译文)

缘由: 官方文档纯英文不方便阅读, 且vue有更好的文档地址Vue Grid Layout -️ 适用Vue.js的栅格布局系统

React-Grid-Layout:

git地址: React-Grid-Layout

README.md

React-Grid-Layout是一个网格布局系统就像 Packery 或者Gridster, 用于React.

不像这些系统, 他是响应式和支持断点.断点布局可以提供给用户自己设置或者自动生成.

RGL(react-grid-layout)是仅仅只需要react而不是需求jquery.

BitMEX UI

GIF from production usage on BitMEX.com

表格目录

Demos

  1. 展示 - Showcase
  2. 基础 - Basic
  3. 禁止拖拽/调大小(仅仅布局) - No Dragging/Resizing (Layout Only)
  4. 凌乱布局自动更正 - Messy Layout Autocorrect
  5. 子节点布局的定义 - Layout Defined on Children
  6. 静态元素 - Static Elements
  7. 新增/移除元素 - Adding/Removing Elements
  8. 保存布局存localstroage - Saving Layout to LocalStorage
  9. 保存响应式布局存localstroage - Saving a Responsive Layout to LocalStorage
  10. 最大化/最小化宽高 = Minimum and Maximum Width/Height
  11. 动态最大最小宽高 - Dynamic Minimum and Maximum Width/Height
  12. 无需垂直角度压缩(自由运动) - No Vertical Compacting (Free Movement)
  13. 预防碰撞 - Prevent Collision
  14. 报错情况 - Error Case
  15. 工具箱 - Toolbox
  16. 从外面拉入 - Drag From Outside
  17. 有界限的布局 - Bounded Layout
  18. 调整大小事件 - Resizable Handles
  19. 缩放容器 - Scaled Containers

项目中使用 React-Grid-Layout

Know of others? Create a PR to let me know!

特色

  • 100% React - no jQuery
  • 兼容服务端渲染App
  • 拖拽组件
  • 拉动大小组件
  • 静态组件
  • 可配置的填充方式: 水平,垂直,或者关闭
  • 拖拽和调试大小有边界
  • 组件增加或者减少没有重新渲染栅栏
  • 布局可以序列化以及恢复Layout can be serialized and restored
  • 响应式断点
  • 每个响应断点的单独布局
  • 栅栏使用c3 transform 来实现拖拽
    • CSS Transforms 性能提现: on / off, note paint (green) as % of time
  • 兼容性 <React.StrictMode>
VersionCompatibility
>= 0.17.0React 16 & 17
>= 0.11.3React 0.14 & 15
>= 0.10.0React 0.14
0.8. - 0.9.2React 0.13
< 0.8React 0.12

安装

Install the React-Grid-Layout package package using npm:

npm install react-grid-layout

在应用中要包括以下列表(css样式):

/node_modules/react-grid-layout/css/styles.css
/node_modules/react-resizable/css/styles.css

使用

使用ReactGridLayout就像其他的组件.他将遵循以下的例子,一个栅栏包含了三个组件:

  • 使用者不能拖拽和调整a的大小
  • 组件b相当于适用一个最小2个栅栏宽度和最大宽度为4个栅栏的元素
  • 使用者可以随意的拖拽和调整c节点的大小
import GridLayout from 'react-grid-layout';

class MyFirstGrid extends React.Component {
  render() {
    // layout 是一个对象数组, 查看演示可以了解更多的用法
    const layout = [
      {i: 'a', x: 0, y: 0, w: 1, h: 2, static: true},
      {i: 'b', x: 1, y: 0, w: 3, h: 2, minW: 2, maxW: 4},
      {i: 'c', x: 4, y: 0, w: 1, h: 2}
    ];
    return (
      <GridLayout className="layout" layout={layout} cols={12} rowHeight={30} width={1200}>
        <div key="a">a</div>
        <div key="b">b</div>
        <div key="c">c</div>
      </GridLayout>
    )
  }
}

你也可以选择直接在子节点上面直接设置属性

import GridLayout from 'react-grid-layout';

class MyFirstGrid extends React.Component {
  render() {
    return (
      <GridLayout className="layout" cols={12} rowHeight={30} width={1200}>
        <div key="a" data-grid={{x: 0, y: 0, w: 1, h: 2, static: true}}>a</div>
        <div key="b" data-grid={{x: 1, y: 0, w: 3, h: 2, minW: 2, maxW: 4}}>b</div>
        <div key="c" data-grid={{x: 4, y: 0, w: 1, h: 2}}>c</div>
      </GridLayout>
    )
  }
}

不使用Browserify/Webpack

一个模块使用只要使用一个<script>包括这里here.他使用umd shim并 不仅仅React-cli, 所以他还可以使用在requireJs中或者是window.React

响应式使用

做响应式RGL,使用<ResponsiveReactGridLayout>元素

import { Responsive as ResponsiveGridLayout } from 'react-grid-layout';

class MyResponsiveGrid extends React.Component {
  render() {
    // {lg: layout1, md: layout2, ...}
    const layouts = getLayoutsFromSomewhere();
    return (
      <ResponsiveGridLayout className="layout" layouts={layouts}
        breakpoints={{lg: 1200, md: 996, sm: 768, xs: 480, xxs: 0}}
        cols={{lg: 12, md: 10, sm: 6, xs: 4, xxs: 2}}>
        <div key="1">1</div>
        <div key="2">2</div>
        <div key="3">3</div>
      </ResponsiveGridLayout>
    )
  }
}

当使用响应式模式, 通过layouts你应该至少提供一个断点

如果最大提供了,RGL会尝试对其他部分插入

你还需要提供一个width,当使用<ResponsiveReactGridLayout>时候,建议你使用HOC高阶组件 WidthProvider,按照下面的说明

WidthProvider as per the instructions below. 通过data-grid属性提供可以支持的属性,所以可以在之后布局插值中使用.

提供栅栏宽度

<ResponsiveReactGridLayout><ReactGridLayout>需要使用width来计算拖拽事件的位置. 简单的用例使用HOC高阶组件WidthProvider可以自动的初始化宽度和窗口调整事件大小.

import { Responsive, WidthProvider } from 'react-grid-layout';

const ResponsiveGridLayout = WidthProvider(Responsive);

class MyResponsiveGrid extends React.Component {
  render() {
    // {lg: layout1, md: layout2, ...}
    var layouts = getLayoutsFromSomewhere();
    return (
      <ResponsiveGridLayout className="layout" layouts={layouts}
        breakpoints={{lg: 1200, md: 996, sm: 768, xs: 480, xxs: 0}}
        cols={{lg: 12, md: 10, sm: 6, xs: 4, xxs: 2}}>
        <div key="1">1</div>
        <div key="2">2</div>
        <div key="3">3</div>
      </ResponsiveGridLayout>
    )
  }
}

让你可以轻松更换WidthProvider,为您自己的 Provider HOC,如果您需要更复杂的逻辑的话。

WidthProvider接受一个 Props,measureBeforeMount。如果为true,WidthProvider将在安装(mount) children 之前,测量容器的宽度。如果您想完全消除 应用程序/组件 mount 上的,任何调整大小动画,请使用此选项。

有一个更复杂的布局?WidthProvider 是很简单的,并且只听监听'resize'的窗口事件。如果您需要更多功能和灵活性,请尝试使用SizeMe React HOC,作为 WidthProvider 的替代品。

栅栏布局Props

RGL支持以下属性(最后一句,请参来源)

//
// 基础 props
//

// 这允许在服务器端设置初始宽度.
// 这个是必须的除非使用`<WidthProvider>`或者类似
width: number,

// 如果是true, 这个容器高度会膨胀并且自动适应
autoSize: ?boolean = true,

// 此布局的列数
cols: ?number = 12,

// 一个css选择器作用于tag上让他禁止拖动
// 举个例子: draggableCancel:'.MyNonDraggableAreaClassName'
// 如果你忘了加`.`,他并不会起作用
draggableCancel: ?string = '',


// 一个css选择器作用于tag上让他拖动
// 举个例子: draggableCancel:'.MyNonDraggableAreaClassName'
// 如果你忘了加`.`,他并不会起作用
draggableHandle: ?string = '',

//如果是true,这个布局会垂直压缩
verticalCompact: ?boolean = true,

// Compaction 压缩方向
compactType: ?('vertical' | 'horizontal') = 'vertical';


// layout 是一个对象数组
// 格式化为{x: number, y: number, w: number, h: number}
// 布局中的索引必须与每个项组件上使用的键匹配。
// 如果选择使用自定义关键点,可以在布局中指定该键
// 对象数组就像这样
// {i: string, x: number, y: number, w: number, h: number}
layout: ?array = null, // 如果没有,请使用data-grid属性作用于child

// items之间magin [x, y]单位默认px
margin: ?[number, number] = [10, 10],

// items之间Padding[x, y]单位默认px 
containerPadding: ?[number, number] = margin,

// 行高有一个默认高度,但是你可以改变他在断点处
// 就像这样
rowHeight: ?number = 150,

// 配置一个可以拖拽的元素.拖拽的元素是一个"真实"元素
// 从外面拖拽一个元素进来的时候
// 你可以将更好的格式化参数
// i -- id
// w -- width
// h -- height
droppingItem?: { i: string, w: number, h: number }

//
// Flags
//
isDraggable: ?boolean = true,
isResizable: ?boolean = true,
isBounded: ?boolean = false,
// 使用Css3 Translate 替换 position
// 这个可以使性能可以提高6倍
useCSSTransforms: ?boolean = true,
// 如果父节点`ResponsiveReactGridLayout`或者`ReactGridLayout`有`transform: scale(n)`css属性
// 我们需要设置比例系数以避免拖动时渲染
transformScale: ?number = 1,

// 如果true,栅栏不会改变位置
// 拖拽也是一样
preventCollision: ?boolean = false;

//
// 标志
//
isDroppable: ?boolean = false

// 定义呈现调整大小就是正常的栅栏布局
// 's' - South handle (bottom-center)
// 'w' - West handle (left-center)
// 'e' - East handle (right-center)
// 'n' - North handle (top-center)
// 'sw' - Southwest handle (bottom-left)
// 'nw' - Northwest handle (top-left)
// 'se' - Southeast handle (bottom-right)
// 'ne' - Northeast handle (top-right)
resizeHandles: ?Array<'s' | 'w' | 'e' | 'n' | 'sw' | 'nw' | 'se' | 'ne'> = ['se']
// 重新制定大小自定义组件
resizeHandle?: ReactElement<any> | ((resizeHandleAxis: ResizeHandleAxis) => ReactElement<any>)

//
// Callbacks
//


// 回调保存布局
// 得到布局数据在拖拽完成
onLayoutChange: (layout: Layout) => void,

// 所有的回调都有签名

type ItemCallback = (layout: Layout, oldItem: LayoutItem, newItem: LayoutItem,
                     placeholder: LayoutItem, e: MouseEvent, element: HTMLElement) => void;

// 拖拽移动开始
onDragStart: ItemCallback,
// 拖拽移动
// Calls on each drag movement.
onDrag: ItemCallback,
// Calls when drag is complete.
onDragStop: ItemCallback,
// Calls when resize starts.
onResizeStart: ItemCallback,
// Calls when resize movement happens.
onResize: ItemCallback,
// Calls when resize is complete.
onResizeStop: ItemCallback,
// Calls when an element has been dropped into the grid from outside.
onDrop: (layout: Layout, item: ?LayoutItem, e: Event) => void

// Ref for getting a reference for the grid's wrapping div.
// You can use this instead of a regular ref and the deprecated `ReactDOM.findDOMNode()`` function.
innerRef: ?React.Ref<"div">

响应式栅栏布局 Props

可以使用响应式栅栏布局。它支持上面的所有 Props 除了layout。新属性和更改是:

// {name: pxVal}, 例如: {lg: 1200, md: 996, sm: 768, xs: 480}
// 断点名称是任意的,但必须在 cols和 layouts 对象中,匹配。
breakpoints: ?Object = {lg: 1200, md: 996, sm: 768, xs: 480, xxs: 0},

// cols 的 #。这是一个断点 -> cols 的 map,例如:{lg: 12, md: 10, ...}
cols: ?Object = {lg: 12, md: 10, sm: 6, xs: 4, xxs: 2},

// margin 可以指定横向和纵向的margin 就像`[10, 10]` or as a breakpoint -> margin map, e.g. `{lg: [10, 10], md: [10, 10], ...}.
margin: [number, number] | {[breakpoint: $Keys<breakpoints>]: [number, number]}

// 内容padding 可以定制横向与纵向`[10, 10]` or as a breakpoint -> containerPadding map, e.g. `{lg: [10, 10], md: [10, 10], ...}.
containerPadding: [number, number] | {[breakpoint: $Keys<breakpoints>]: [number, number]}

// layouts是一个对象数组
// e.g. {lg: Layout, md: Layout, ...}
layouts: {[key: $Keys<breakpoints>]: Layout}

//
// Callbacks
//

// Calls back with breakpoint and new # cols
onBreakpointChange: (newBreakpoint: string, newCols: number) => void,

// Callback so you can save the layout.
// AllLayouts are keyed by breakpoint.
onLayoutChange: (currentLayout: Layout, allLayouts: {[key: $Keys<breakpoints>]: Layout}) => void,

// 宽度更改时回调,以便根据需要,修改布局。
onWidthChange: (containerWidth: number, margin: [number, number], cols: number, containerPadding: [number, number]) => void;

栅栏项Props

RGL 的网格项或布局项支持以下属性。初始化网格时,构建一个布局数组(如上面的第一个示例中,所示),或者将此对象附加到,每个子元素的data-grid属性(如第二个示例中,所示)。

请注意,如果提供的网格项不完整(缺少其中一个)x, y, w, or h),将抛出错误,以便更正您的布局。

如果没有为网格项,提供属性,则将生成宽度和高度为1的网格项。

您可以为每个层级,设置最小值和最大值。这是为了调整大小;如果禁用调整大小,它当然没有效果。如果您的最小值和最大值,重叠不正确,或者您的初始尺寸超出范围,则会引发错误。

<GridItem>直接定义的任何属性,优先于全局设置选项。例如,如果布局具有isDraggable: false属性,但网格项 Props 有 isDraggable: true,该项将是可拖动的。

{

  // 一个 字符串,对应组件的 key
  i: string,

  // 在 grid 单元中的所有, 不是 像素(px)
  x: number,
  y: number,
  w: number,
  h: number,
  minW: ?number = 0,
  maxW: ?number = Infinity,
  minH: ?number = 0,
  maxH: ?number = Infinity,

  // 如果为 true, 等于`isDraggable: false, isResizable: false`.
  static: ?boolean = false,
  // 如果为 false, 则不能拖放 draggable. 被 `static` 覆盖.
  isDraggable: ?boolean = true,
  // 如果为 false, 则不能调整大小 resizable. 被 `static` 覆盖.
  isResizable: ?boolean = true,

  resizeHandles?: ?Array<'s' | 'w' | 'e' | 'n' | 'sw' | 'nw' | 'se' | 'ne'> = ['se']
  // 如果为true item只会在网络中移动
  isBounded: ?boolean = false
}

性能

<ReactGridLayout> has an optimized shouldComponentUpdate implementation, 但它依赖于用户 children 数组:

// lib/ReactGridLayout.jsx
// ...
shouldComponentUpdate(nextProps: Props, nextState: State) {
  return (
    // 笔记: 这个几乎是不平等的,因此这是获取性能唯一途径
    // 从SCU 如果用户有意将子对象记忆起来。如果他们这样做了,他们可以
    // 正确处理更改,性能将提高。
    this.props.children !== nextProps.children ||
    !fastRGLPropsEqual(this.props, nextProps, isEqual) ||
    !isEqual(this.state.activeDrag, nextState.activeDrag)
  );
}
// ...

如果你有记忆你的子节点,你可以利用这点,会更快的render

比如:

function MyGrid(props) {
  const children = React.useMemo(() => {
    return new Array(props.count).fill(undefined).map((val, idx) => {
      return <div key={idx} data-grid={{x: idx, y: 1, w: 1, h: 1}} />;
    });
  }, [props.count]);
  return <ReactGridLayout cols={12}>{children}</ReactGridLayout>;
}

因为children属性在两次重播之间不会改变,更新<MyGrid>不会导致新的渲染,提高性能

Because the children prop doesn't change between rerenders, updates to <MyGrid> won't result in new renders, improving performance.

贡献

如果您有功能请求,请将其添加为问题或提出请求。

如果你有bug请求,请修复这个bug在CodeSandbox 去帮助我们更简单的完善他.

待办事项列表

  • Basic grid layout
  • Fluid grid layout
  • Grid packing
  • Draggable grid items
  • Live grid packing while dragging
  • Resizable grid items
  • Layouts per responsive breakpoint
  • Define grid attributes on children themselves (data-grid key)
  • Static elements
  • Persistent id per item for predictable localstorage restores, even when # items changes
  • Min/max w/h per item
  • Resizable handles on other corners
  • Configurable w/h per breakpoint