简介
这个库恰好最近看到别人的分析比较多,觉得也比较值得看一下
拖拽库
- react-dnd 除了列表操作,还有拖拽进去,随意拖拽某个位置,
- react-beautiful-dnd 专注于列表拖拽 貌似是后起之秀
- react-draggable 容器内位置的拖拽
Sortable
React Dnd
核心概念
https://react-dnd.github.io/react-dnd/docs/overview
- item是看到可拖拽的对象,原始就是一个js的对象,而不是dom或其他
- type即定义类型用来区分不同的drag和drop,以及drag和drop的匹配
- monitor 即保存了拖拽对象是否拖拽中状态,以及拖放容器状态,并暴露给组件来响应state的变化
- Connectors?这是啥?
- Drag Sources and Drop Targets 比较弱化了
- Backends 不同的拖砖容器,有html5或touch
- Hooks vs Higher-Order Components Decorator
提供API的模式
Hooks
Decorator
思考这两者的特性
核心的对象monitor
- DragSourceMonitor
canDrag
isDragging
getItemType
getItem 当前拖拽的
getDropResult
getClientOffset
- DropTargetMonitor
canDrop
isOver
getItemType
getItem 当前拖拽的对象
getDropResult
- DragLayerMonitor
如何分层的 DragDropManager
backend ->
store -> redux的store,reducers和actions
monitor -> SourceMonitor, TargetMonitor
manager -> DragDropManager
Connector -> SourceConnector,TargetConnector
背后的核心dragdropManager
export function createDragDropManager(
backendFactory: BackendFactory,
globalContext: unknown = undefined,
backendOptions: unknown = {},
debugMode = false,
): DragDropManager {
const store = makeStoreInstance(debugMode)
const monitor = new DragDropMonitorImpl(store, new HandlerRegistryImpl(store))
const manager = new DragDropManagerImpl(store, monitor)
const backend = backendFactory(manager, globalContext, backendOptions)
manager.receiveBackend(backend)
return manager
}
// 1 store, 即redux定义的store,在reducers中的数据,如下的state
// 即整个store
export interface State {
dirtyHandlerIds: DirtyHandlerIdsState
dragOffset: DragOffsetState // 当前拖拽的
refCount: RefCountState
dragOperation: DragOperationState
stateId: StateIdState
}
// 2 monitor 提供访问当前拖拽状态的对象,拖拽的对象,拖拽的位置等
// registry, drag, drop实例都会注册到registry属性
// 背后通过访问registry以及store,来获取各信息,也就是个中间商,封装简化对内部store中数据的访问,
// 3 actions 即redux中的action,抽象了各种操作的actions,beginDrag,endDrag,drop等,然后action会处理数据逻辑
// 最终返回payload,通过dispatch更新到store中
// 4 backend 即html5等backend,抽象了底层各平台的细节差异,
// 以html5-backend为例
//
有哪些设计模式等学习的
beginDrag中的createBeginDrag的factory工厂模式
整个流程是如何的?如果你拖动了一个元素会发生什么?
connectDragSource(sourceId, node, options) -> handleDragStart handleSelectStart
1 handleDragStart dragStartSourceIds.unshift(sourceId)
2 this.actions.beginDrag(dragStartSourceIds) 这背后做了很多逻辑
3
handleTopDragStart 即window上绑定的事件
export class HTML5BackendImpl implements Backend {
// React-Dnd Components
private actions: DragDropActions
private monitor: DragDropMonitor
private registry: HandlerRegistry
private sourcePreviewNodes: Map<string, Element> = new Map()
private sourceNodes: Map<string, Element> = new Map()
private dragStartSourceIds: string[] | null = null
private dropTargetIds: string[] = []
private currentDragSourceNode: Element | null = null
public connectDragSource(
sourceId: string,
node: Element,
options: any,
): Unsubscribe {
this.sourceNodes.set(sourceId, node)
this.sourceNodeOptions.set(sourceId, options)
const handleDragStart = (e: any) => this.handleDragStart(e, sourceId)
const handleSelectStart = (e: any) => this.handleSelectStart(e)
node.setAttribute('draggable', '' + this.monitor.canDragSource(sourceId))
node.addEventListener('dragstart', handleDragStart)
node.addEventListener('selectstart', handleSelectStart)
// 返回解绑的方法
return ...
}
public handleTopDragStart = (e: DragEvent): void => {
// Don't publish the source just yet (see why below)
this.actions.beginDrag(dragStartSourceIds || [], {
publishSource: false,
getSourceClientOffset: this.getSourceClientOffset,
clientOffset,
})
if (this.monitor.isDragging()) {
if (dataTransfer && typeof dataTransfer.setDragImage === 'function') {
const sourceId: string = this.monitor.getSourceId() as string
const sourceNode = this.sourceNodes.get(sourceId)
const dragPreview = this.sourcePreviewNodes.get(sourceId) || sourceNode
...
}
}
}
}
React Beautiful Dnd
特征?
性能高?虚拟
简洁的API
强大的场景和功能
核心概念
- DragDropContext 核心的manager,onDragEnd
- 一个DragDropContext里可以有多个Droppable
- Droppable
- provided,snapshot,droppableId
- Draggable
- provided,snapshot,draggableId
https://codesandbox.io/s/ql08j35j3q 多个Droppable,即两列进行拖拽
https://codesandbox.io/s/k260nyxq9v 最简单的入门demo,可拖拽排序的垂直列表
https://github.com/atlassian/react-beautiful-dnd/blob/master/docs/about/design-principles.md 设计原则
为什么有了react-dnd,还做了这个?
react-beautiful-dnd is a higher level abstraction specifically built for lists (vertical, horizontal, movement between lists, nested lists and so on)
React Dnd VS React Beautiful Dnd
DnD | Beautiful Dnd | |
---|---|---|
核心概念 | - DndProvider - Drag - Drop 通过此类处理事件 - DragDropManager 背后隐藏的 |
- DragDropContext 核心的manager,通过此来处理事件 - Draggable Component - Droppable Component |
暴露方式 | hooks或hoc | render props |
drop对象处理 | 顶层context处理 | |
排序移动处理方式 | 拖拽排序需要自己写很多处理逻辑,位置判断等,有点偏底层。概念用drop也不太好理解 排序的example |
onDragEnd只暴露startIndex, endIndex,自己处理移动的数据操作 |
两个不同的设计思想
React Draggable
借鉴
http://www.ayqy.net/blog/react-dnd/
https://medium.com/@alexandereardon/rethinking-drag-and-drop-d9f5770b4e6b
https://www.npmtrends.com/react-beautiful-dnd-vs-react-dnd-vs-react-draggable