背景介绍
文章 pro-components 踩坑 中介绍了 @ant-design/pro-components 中的ProTable正好具备自定义列的功能,然而由于ProComponents功能全面且强大,导致内部包高耦合,包的大小很大,安装和打包需要占用大量的时间,虽然可以通过在config.ts中配置externals来解决,externals通过 排除某些 import 的包(package)打包到bundle中,而在运行时(runtime)再去从外部获取扩展依赖,可以大幅度提高打包效率,而外部依赖可以通过cdn引入提高加载速度。
但是外部引入cdn 存在一定的安全隐患,同时外部加载cdn依赖容易 受到用户侧网络问题、以及 cdn域名访问速度过慢的问题从而导致 一定的白屏时间占用;另一个问题是通过externals的方式配置cdn引入,一旦改了package.json中某个包的版本,对应的cdn引入文件也要手动修改,操作较为繁琐,因此手写自定义列表格可能是最优解,而自定义列最复杂、繁琐的功能莫过于拖拽,这里选取了@dnd-kit 作为拓展工具包。
@dnd-kit 介绍
dnd kit 是一个用于 React 的现代、轻量级、高性能、可访问和可扩展的拖放工具包。
- 为 React 构建: 公开诸如 useDraggable和 useDroppable之类的 hooks,并且不需要你重新构建应用程序或创建额外的包装器 DOM 节点。
- 功能丰富: 可定制的碰撞检测算法、多个激活器、可拖动覆盖、拖动手柄、自动滚动、约束等等。
- 支持广泛的用例: 列表、网格、多个容器、嵌套上下文、可变大小的项目、虚拟化列表、2D 游戏等。
- 零依赖和模块化: 库的核心重约 10kb,没有外部依赖。它围绕内置的 React 状态管理和上下文构建,从而使库保持精简。
- 内置支持多种输入法: 指针、鼠标、触摸和键盘传感器。
- 完全可定制和可扩展: 定制每个细节:动画、过渡、行为、样式。构建你自己的传感器、碰撞检测算法、自定义键绑定等等。
- 辅助功能: 键盘支持、合理的默认咏叹调属性、可定制的屏幕阅读器说明和内置实时区域。
- 性能: 它的构建考虑了性能,以支持丝般流畅的动画。
- 预设: 需要构建一个可排序的界面?Check out @dnd-kit/sortable,这是建立在 @dnd-kit/core 上面的 thin layer。未来会有更多预设。
dnd kit 拖拽使用说明
- 使用前提
下载以下5个依赖:
@dnd-kit/core
@dnd-kit/accessibility
@dnd-kit/sortable
@dnd-kit/modifiers
@dnd-kit/utilities
- 标签/组件介绍
1、DndContext:dnd kit的容器,使用拖拽操作必须要有,并且滑动列表的拖拽事件都在这里写(onDragMove,onDragEnd等等)
2、SortableContext:滑动列表的容器,相当于ul,内部的列表配置好了可以拖拽(items为必填项,表示哪些列表在我这个容器中滑动,items为唯一标识的数组)
3、useSortable:将唯一标志变为一个列表的内部项,即将SortableContext中提供的items的子项渲染,相当于li,使用方式:useSortable({id:XXX})。
返回值说明: 1、setNodeRef:关联dom节点,使其成为一个可拖拽的项;
2、listeners:包含onKeyDown,onPointerDown方法,主要让节点可以进行拖拽;
3、transform:该节点被拖动时候的移动变化值;
4、transition:过渡效果;
5、isDragging:节点是否在拖拽
4、sensors:dndkit提供的传感器,默认是使用所有传感器,包括鼠标、键盘等
5、DragOverlay:相当于不更改原对象,当拖拽时会复制一个新的div
6、collisionDetection:碰撞算法,具体可查看相关文档
7、arrayMove:拖拽后交换数组的位置,进行上移和下移,内部代码如下:
function arrayMove(array, from, to) {
const newArray = array.slice();
newArray.splice(to < 0 ? newArray.length + to : to, 0, newArray.splice(from, 1)[0]);
return newArray;
}
- 简单demo代码:
import { DndContext } from "@dnd-kit/core";
import { arrayMove, SortableContext, useSortable } from "@dnd-kit/sortable";
import { CSS } from "@dnd-kit/utilities";
import { useState } from "react";
// 容器组件
export default function SingleTest() {
const [items,setItems] = useState(["A","B","C"])
// 拖拽结束后的操作
function dragEndEvent(props:any) {
const { active,over } = props
const activeIndex = items.indexOf(active.id)
const overIndex = items.indexOf(over.id)
setItems(items=>{
return arrayMove(items,activeIndex,overIndex)
})
}
return (
<DndContext onDragEnd={dragEndEvent}>
<SortableContext items={items}>
{
items.map(val=>(<Item id={val}/>))
}
</SortableContext>
</DndContext>
)
}
// 拖拽项组件
function Item(props:any) {
const { id } = props
const {setNodeRef,listeners,transform,transition } = useSortable({id})
const styles = {
transform:CSS.Transform.toString(transform),
border: "1px solid red",
marginTop: "10px"
}
return (
<div ref={ setNodeRef } {...listeners} style={styles}>{id}</div>
)
}
注意事项:
-
点击事件失效
原因:点击事件onClick和拖拽事件的onPointerDown有部分重叠,导致点击的时候系统无法准确的知道你是click还是pointerDown
解决方法:测试下来方法2效果更加
//1.添加 sensor 传感器,增加一个延迟。 const sensors = useSensors(useSensor(PointerSensor,{ activationConstraint: { delay: 100, tolerance: 0, } })) //2.使用传感器,设置鼠标传感器,当距离小于5时不响应拖拽事件 const sensors = useSensors(useSensor(PointerSensor,{ activationConstraint: { distance: 5, } })) -
解决系统自定义CSS方法带来的问题
// 只需要重写单个拖拽项的styles即可(弃用系统自带CSS方法) function Item(props:any) { const { id } = props const {setNodeRef,listeners,transform,transition } = useSortable({id}) const styles = isDragging?{ transform: `translate3d(0px, ${transform?.y}px, 0) scaleX(1) scaleY(1)`, border: "1px solid red", marginTop: "10px" }:undefined return ( <div ref={ setNodeRef } {...listeners} style={styles}>{id}</div> ) } -
子项item使用checkbox时,拖拽前点击正常,拖拽后点击事件失效
可能原因:由于代码中通过checkbox.group 组件统一管理checkbox,拖拽时会重新复制子项的chebox,从而产生了子项checkbox的onClick、onChange事件丢失、失效的现象。解决方法:取消checkbox.group 组件统一管理checkbox,单独对每个checkbox设置onClick、onChange事件
<Checkbox className="checkbox" value={item.id} checked={checkList.includes(item.id)} onChange={() => checkChange(item.id)} //单独设置 > {item.title} </Checkbox> -
window、mac系统 滚动条样式问题
原因:在不同的操作系统中,浏览器的滚动条样式的差异,主要是由于操作系统本身的设计和样式导致的。在Windows和macOS中,滚动条的样式是不同的。在macOS中,滚动条是不占据屏幕尺寸的,而在Windows中,滚动条占据一定的屏幕尺寸
解决方法:如果我们想要在不同的操作系统中,使浏览器的滚动条样式一致,我们可以使用CSS来自定义滚动条。在全局的CSS样式中,我们可以使用::-webkit-scrollbar来定义滚动条整体的样式,使用::-webkit-scrollbar-thumb来定义滚动条拇指的样式,使用::-webkit-scrollbar-track来定义滚动条轨道的样式。
/* 滚动条,抹平不同系统差异 */ .list-group::-webkit-scrollbar { /* 隐藏默认的滚动条 */ -webkit-appearance: none; } .list-group::-webkit-scrollbar:vertical { /* 设置垂直滚动条宽度 */ width: 6px; } .list-group:hover::-webkit-scrollbar-thumb:vertical { /* 滚动条的其他样式定制,注意,这个一定也要定制,否则就是一个透明的滚动条 */ border-radius: 6px; // border: 8px solid rgba(255, 255, 255, 0.5); background-color: rgba(0, 0, 0, 0.4); }
自定义列实现原理
查看ProTable源码得通过三个字段{"show":true,"fixed":"left","order":0}控制自定义列,即show控制显示与否,fixed控制左右固定,order控制字段显示顺序,核心代码位置如下
手写的自定义列借鉴了ProTable的实现,也通过{"show":true,"fixed":"left","order":0}三个字段控制显示、固定、展示顺序。
github 源码地址
采用的ts编写,ts较为薄弱,any较多,大佬们多多包含😊,使用中如若存在问题call me,目前测试次数较少。
重要的事情说三遍:求star😄!
参考链接
docs.dndkit.com/introductio…
procomponents.ant.design/components/…
juejin.cn/post/716652…
juejin.cn/post/709386…