异步复制ProseMirror元素时剪贴板数据写入超时问题
异步复制ProseMirror元素时剪贴板数据写入超时问题
这个问题的核心痛点其实是浏览器的copy事件是同步触发的——当你在事件回调里发起异步请求(比如从存储拉取图片转base64)时,事件本身早就已经执行完毕了,这时候再去写剪贴板自然就赶不上了。我来给你几个可行的解决思路和具体实现方案:
核心思路:先拦截默认复制,异步完成后手动写入剪贴板
我们需要先阻止浏览器的默认复制行为,等所有异步资源处理完成后,再用现代剪贴板API手动把处理好的ProseMirror节点数据写入剪贴板。
具体实现步骤
拦截默认
copy事件
先给ProseMirror编辑器绑定copy事件,用event.preventDefault()阻止浏览器的默认复制流程,这样我们就能完全掌控剪贴板的内容了。异步批量处理图片资源
遍历要复制的ProseMirror节点,找出所有image类型的节点,用Promise.all并行发起异步请求获取base64资源,确保所有图片都处理完成后再继续后续操作。用现代剪贴板API写入数据
所有异步操作完成后,把处理好的节点序列化为你需要的JSON格式,再用navigator.clipboard.write写入自定义MIME类型的数据,同时可以额外写入纯文本/HTML格式,让其他应用也能识别粘贴内容。
完整代码示例
// 给ProseMirror编辑器绑定copy事件 editor.view.dom.addEventListener('copy', async (event) => { // 1. 阻止浏览器默认复制行为,接管剪贴板控制权 event.preventDefault(); try { // 2. 获取当前选中的ProseMirror内容片段 const { state } = editor.view; const { from, to } = state.selection; const selectedContent = state.doc.slice(from, to); // 3. 遍历节点,收集所有图片的异步处理Promise const imageProcessPromises = []; // 先把节点转成可修改的数组,方便后续替换 const contentNodes = selectedContent.content.toArray(); contentNodes.forEach((node, index) => { if (node.type.name === 'image') { // 替换成你从存储获取base64的异步方法 const processImage = async () => { const base64Src = await yourStorageFetchFunction(node.attrs.src); // 生成处理后的新图片节点 const newImageNode = node.type.create({ ...node.attrs, src: base64Src }); // 替换原数组中的节点 contentNodes[index] = newImageNode; }; imageProcessPromises.push(processImage()); } }); // 等待所有图片异步处理完成 await Promise.all(imageProcessPromises); // 4. 将处理好的节点序列化为目标JSON格式 const jsonOutput = JSON.stringify( contentNodes.map(node => JSON.stringify(node.toJSON())) ); // 5. 写入剪贴板:自定义格式 + 兼容其他应用的纯文本格式 const clipboardContents = [ // 写入你的应用自定义格式 new ClipboardItem({ 'text/my-app': new Blob([jsonOutput], { type: 'text/plain' }) }), // 写入纯文本,让其他应用也能粘贴到内容 new ClipboardItem({ 'text/plain': new Blob([selectedContent.textContent], { type: 'text/plain' }) }) ]; // 手动写入剪贴板 await navigator.clipboard.write(clipboardContents); } catch (error) { console.error('复制操作失败:', error); // 降级处理:如果现代剪贴板API不可用,用同步方式复制纯文本 event.clipboardData.setData('text/plain', selectedContent.textContent); } });
关键细节说明
Promise.all的作用:确保所有图片的异步请求都完成后再处理剪贴板,避免出现部分图片未加载完成就写入的情况。ClipboardItem兼容性:这个API需要HTTPS环境(localhost开发环境例外),如果要兼容旧浏览器,可保留event.clipboardData.setData作为降级方案,但旧浏览器无法支持异步场景的完整处理。- ProseMirror节点操作:通过
slice获取选中内容,用toArray转为可修改的数组,处理后再序列化,完全匹配你原本的JSON输出格式。
这样处理后,就能完美解决异步操作赶不上剪贴板事件生命周期的问题了!
备注:内容来源于stack exchange,提问作者Gaurussel
相关产品推荐
相关产品推荐

