市面排序方案比较:
| 产品特性 | react-dnd | react-beautiful-dnd | dnd-kit |
|---|---|---|---|
| 兼容性 | 触摸和 IE10+ | 最新稳定版本的浏览器 | 指针、鼠标、触摸、键盘 |
| 包体积 | 900KB | 1.39 MB | 1MB |
| 代码复杂度 | 复杂 | 较复杂 | 较复杂 |
一口气吃不成胖子,我们选择相对简单的dnd-kit来上手
安装dnd-kit:
| 安装目录 | 安装命令 | 一句话描述用途 |
|---|---|---|
| 核心 | npm install @dnd-kit/core | 实现基本的拖拽 |
| 扩展 | npm install @dnd-kit/sortable | 优化排序 |
| 工具 | npm install @dnd-kit/utilities | 实现拖拽视觉效果 |
核心步骤:
1.编写父组件:
1.1明确可拖拽区域,在可拖拽区域外层包裹DndContext和SortableContext:
1.2 配置DndContext组件的属性onDragEnd(拖拽结束事件的回调函数,必传),在回调中完成拖拽的交换逻辑
2.编写子组件:
这里比较简单,从父组件传进来的props里取出id交给useSortable,给子组件最外层div带上ref、attributes、style属性即可,需要注意的是{...listeners}放在哪个元素上,哪个元素就是拖拽的抓手
代码贴在下面: 父组件代码:
import { DATA } from "../../const/data.js";
import SortItem from "../SortItem/index.jsx";
import { DndContext } from "@dnd-kit/core";
import {
SortableContext,
verticalListSortingStrategy,
arrayMove,
} from "@dnd-kit/sortable";
import "./index.css";
import { useState } from "react";
const SortList = () => {
const [list, setList] = useState(DATA);
const handleDragEnd = (dragItem) => {
const { active, over } = dragItem;
if (!active || !over) return; // 处理边界情况
const activeIndex = list.findIndex((item) => item.id === active.id);
const overIndex = list.findIndex((item) => item.id === over?.id);
const moveList = [...list];
if (activeIndex !== overIndex) {
const newList = arrayMove(moveList, activeIndex, overIndex);
setList(newList);
}
};
return (
<DndContext onDragEnd={handleDragEnd}>
<SortableContext
items={list.map((item) => item.id)}
strategy={verticalListSortingStrategy}
>
{/* 可拖拽区域 */}
<div className="list-nav">
{list.map((item) => (
<SortItem key={item.id} id={item.id} content={item.content} />
))}
</div>
</SortableContext>
</DndContext>
);
};
export default SortList;
父组件的css:
.list-nav {
display: flex;
flex-direction: column;
width: 200px;
gap: 10px;
}
子组件代码:
import { useSortable } from "@dnd-kit/sortable";
import { CSS } from "@dnd-kit/utilities";
import drag from "../../assets/drag.svg";
import "./index.css";
const SortItem = (props) => {
const { id, content } = props;
const { setNodeRef, attributes, listeners, transform, transition } =
useSortable({
id,
});
const style = {
transform: CSS.Transform.toString(transform),
transition,
};
return (
<div ref={setNodeRef} {...attributes} style={style} className="list-item">
<span>{content}</span>
<img src={drag} {...listeners} />
</div>
);
};
export default SortItem;
子组件css:
.list-item {
position: relative;
border-radius: 8px;
width: 100%;
box-shadow: 0 1px 2px 0 rgba(65, 218, 42, 0.05);
display: flex;
align-items: center;
justify-content: center;
font-size: 24px;
line-height: 40px;
color: blanchedalmond;
height: 40px;
background-color: rgba(33, 140, 124, 0.6);
}
.list-item img {
position: absolute;
right: 8px;
width: 30px;
height: 30px;
}