3行代码给Tiptap装上手柄,拖拽排序还能这样玩
【免费下载链接】tiptapThe headless rich text editor framework for web artisans.项目地址: https://gitcode.com/GitHub_Trending/ti/tiptap
Tiptap 的 DragHandle 扩展把编辑器里每一个块级元素都变成"可抓取的对象":光标扫到哪个段落或列表,它就在旁边浮出一个拖拽手柄,按住一甩就能改变顺序。这篇指南按"装好就能看 → 调行为 → 排嵌套 → 查故障"四段展开,装完即用的部分只需要三行代码。
三十秒看到手柄出现 🚀
结论:装一个基座包加一个框架包,把手柄组件挂进模板,全程不到一分钟。
npm install @tiptap/extension-drag-handle @tiptap/extension-drag-handle-vue-3Vue 3 项目里这样接入,React 把最后一行换成@tiptap/extension-drag-handle-react的同名组件即可:
<template> <drag-handle :editor="editor"> <div class="my-handle" /> </drag-handle> <editor-content :editor="editor" /> </template> <script setup> import { Editor, EditorContent } from '@tiptap/vue-3' import StarterKit from '@tiptap/starter-kit' import { DragHandle } from '@tiptap/extension-drag-handle-vue-3' const doc = '<h1>第一章</h1><p>拖我试试</p><p>第二块内容</p>' const editor = new Editor({ content: doc, extensions: [StarterKit], }) </script>光标移进任意一行,左侧就会浮出一个小圆点,按住往上拖,段落就跟着换位置了。手柄的 DOM 由render()产出、位置由 floating-ui 计算,这段逻辑全部封装在 drag-handle.ts 里,平时你不用碰它。
手柄长什么样、停在哪:三个旋钮
结论:外观、位置、锁死开关分别对应render、computePositionConfig、locked三个选项,各管一摊。
- 想换图标或加背景色,重写
render()返回自己的元素就行——官方 demo 里用 CSS::after画了一个 ⠿ 点阵,代码可参考 DragHandle 的 Vue 演示; - 手柄默认贴在元素左侧顶部(
placement: 'left-start')。做从右往左读(RTL)的编辑器时,把它改成right-start,不然手柄会挤到文字上; - 临时冻结整个拖拽功能,把
locked设成true,或者走命令式(下一节细说)。
顺手埋两个钩子方便埋点和保存:onNodeChange在光标掠过不同块时告诉你当前悬停的节点,onElementDragStart/onElementDragEnd在甩动开始和落定时各回调一次。所有选项的完整签名见 options.ts。
列表、引用里的嵌套块怎么抓 🧩
结论:嵌套拖拽由nested开关控制,"抓哪一层"由一套打分规则决定,你只需要在冲突时补一条规则。
DragHandle.configure({ nested: { allowedContainers: ['bulletList', 'orderedList', 'blockquote'], edgeDetection: 'left', }, })默认只抓顶层块,nested打开后任何深度的块都能拖。规则系统的工作方式是:每个候选节点先拿 1000 分底分,再依次被"列表包装层降权、表格结构排除"这类内置规则扣分,最后分数最高者胜出;想排除某些节点,就写一条返回 1000 的自定义rules。一个很实用的细节是edgeDetection:光标贴近块左边缘时,深层子节点会被多扣strength × 深度的分,于是外层列表更容易被抓到——"贴近边缘抓父级,居中抓子级"。这套打分的完整参数(阈值、强度、可用边缘)都注释在 options.ts,直接读注释就能配。
手柄不见了?四个高频问题对号入座 🛠
结论:九成故障出在"装了基座包却忘了装框架包"或扩展没进extensions数组。
- 手柄完全不出现→ 先确认框架组件已安装且
editor实例非空;纯 JS 场景则检查DragHandle是否真的加进了extensions。入口文件是 index.ts,导出即基座包本身; - 拖完位置错乱、手柄漂移→ 手柄定位走 floating-ui,调整
computePositionConfig的placement,RTL 布局记得换成右侧; - React 里组件报 undefined→ 你多半
import了基座包,换成@tiptap/extension-drag-handle-react的同名组件; - 拖长文档时拖影卡顿→ 拖拽图片默认会复制全部计算样式,用
dragImageProperties只保留color、background-color等少数属性即可提速。
进阶两招:命令式锁与性能微调
结论:lockDragHandle三兄弟适合做"编辑/预览"切换,dragImageProperties适合大文档降负载。
editor.commands.toggleDragHandle() // 锁定与解锁来回切这三个命令(lockDragHandle、unlockDragHandle、toggleDragHandle)内部就是翻转locked选项并广播 meta 事件,挂在"只读预览"按钮上最顺手。再往后的探索方向:给协作场景叠加extension-collaboration,或阅读 drag-handle-plugin.ts 看拖拽状态机如何与 ProseMirror 事务对接。
上线前过一遍这份清单
@tiptap/extension-drag-handle与对应框架包(-vue-3/-vue-2/-react)版本一致;- 手柄组件与
EditorContent在同一个组件模板里,且editor已就绪; - RTL 页面已把
placement翻到右侧; - 需要嵌套拖拽时显式配置了
allowedContainers,避免"乱抓"; onElementDragEnd里接了保存逻辑,拖完的位置不会丢。
【免费下载链接】tiptapThe headless rich text editor framework for web artisans.项目地址: https://gitcode.com/GitHub_Trending/ti/tiptap
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考