用户复制了你 AI 助手生成的代码块,粘贴到 VS Code 里很完美,但粘贴到 Word 或飞书里时,背景颜色和字体格式全丢了。
传统的 document.execCommand('copy') 只能勉强处理简单的文本。要实现"一次复制,多处适配",必须动用现代的 Clipboard API 。它的核心逻辑是构建一个包含多种 MIME 类型的 ClipboardItem。
1. 核心原理:MIME 类型多路复用
当你向系统剪贴板写入数据时,剪贴板本质上是一个 "键值对容器" 。你可以同时塞入 text/plain(保底)、text/html(带格式)甚至 image/png。目标软件在执行"粘贴"时,会根据自身能力选择最合适的格式。
2. 代码实现:封装一个"万能复制"器
现代 API 要求数据必须以 Blob 形式存在。注意:navigator.clipboard.write 接受的是一个数组,而数组里的每个 ClipboardItem 又是一个键值对对象。
JavaScript
javascript
async function copyRichText(plainText, htmlContent) {
// 1. 权限校验
if (!navigator.clipboard) {
console.error("当前环境不支持 Clipboard API");
return;
}
try {
// 2. 将字符串包装为指定类型的 Blob
const plainBlob = new Blob([plainText], { type: 'text/plain' });
const htmlBlob = new Blob([htmlContent], { type: 'text/html' });
// 3. 构建多格式 ClipboardItem
const item = new ClipboardItem({
'text/plain': plainBlob,
'text/html': htmlBlob
});
// 4. 执行写入
await navigator.clipboard.write([item]);
console.log("多格式数据已写入剪贴板");
} catch (err) {
console.error("复制失败:", err);
}
}
// 调用示例:
copyRichText(
"const a = 1;",
"<pre style='color:red; background:#f0f0f0;'><code>const a = 1;</code></pre>"
);
3. 踩坑总结
① Safari 的"必须由用户手势触发"限制
Safari 对安全性的要求极其严苛。如果你在异步请求(如 fetch)的回调里执行复制,Safari 会因为"丢失用户手势上下文"而直接报错。
- 对策 :在 AI 场景下,不要等全文生成完才允许复制。或者在点击事件里立即创建一个
ClipboardItem占位,等异步数据回来后再resolve它。
② HTML 的样式隔离问题
粘贴到邮件客户端或 Office 时,外部 CSS 文件是不生效的。
- 对策 :所有的富文本 HTML 必须使用 Inline Style(行内样式) 。如果你用的是
markdown-it,可以配合一个简单的正则将类名替换为具体的样式字符串。
③ 权限静默失败
在某些 iframe(如低版本的微前端环境)或者非 HTTPS 环境下,navigator.clipboard 是 undefined。
- 对策 :始终提供降级方案。如果新 API 不可用,回退到创建一个不可见的
textarea执行execCommand('copy')。
4. 方案对比:为什么不建议用老方法?
| 维度 | execCommand('copy') | Clipboard API (现代) |
|---|---|---|
| 异步支持 | 极差(必须同步执行) | 原生 Promise 支持 |
| 多格式存取 | 几乎不可能(只能存单一格式) | 完美支持(Plain/HTML/Image) |
| 主线程影响 | 可能会引起布局抖动(Layout Thrashing) | 全异步处理,不阻塞 UI |
| 安全性 | 较低(脚本可静默嗅探) | 严格权限控制(Permissions API) |
5. 高阶应用:剪贴板脱敏
在处理敏感数据(如银行账号)时,你可以利用这个 API 做一层**"视觉欺骗"**:
- 用户选中的是:
6222 0210 0000 1234 - 你存入
text/plain的是:6222 0210 **** 1234 - 你存入
text/html的是:带有红色星号样式的脱敏文本。
这种策略既保护了数据隐私,又保留了良好的用户提示体验。