clipboard、clipboard-polyfill、copy-to-clipboard、react-copy-to-clipboard 和 vue-clipboard2 都是用于在浏览器中执行复制文本到系统剪贴板操作的 JavaScript 库。它们解决了现代 Web 应用中常见的“一键复制”需求,但实现方式、兼容性策略、框架集成度和 API 设计各有不同。这些库封装了浏览器原生 Clipboard API 或其降级方案(如使用临时 <textarea>),以提供一致的跨浏览器体验。
在 Web 应用中实现“复制到剪贴板”看似简单,实则充满兼容性陷阱。不同浏览器对 Clipboard API 的支持程度不一,旧版浏览器甚至完全不支持。本文从真实工程角度,对比五款主流剪贴板库的核心机制、适用场景与技术取舍。
所有库最终都依赖两种底层技术之一:
navigator.clipboard.writeText())— 安全、简洁,但需 HTTPS 且部分旧浏览器不支持。<textarea>,选中其内容后调用 document.execCommand('copy')。各库如何选择和封装这两种方式?
clipboard基于 document.execCommand,通过监听 DOM 事件触发复制。要求目标元素通过 data-clipboard-text 或 data-clipboard-target 声明内容。
// HTML
<button id="btn" data-clipboard-text="要复制的内容">复制</button>
// JS
import Clipboard from 'clipboard';
new Clipboard('#btn'); // 自动绑定点击事件
clipboard-polyfill优先使用 navigator.clipboard.writeText(),失败时自动降级到 execCommand 方案。提供统一的 Promise 接口。
import { writeText } from 'clipboard-polyfill';
writeText("要复制的内容")
.then(() => console.log("成功"))
.catch(err => console.error("失败", err));
copy-to-clipboard内部自动检测环境:若支持 navigator.clipboard 则使用,否则回退到 execCommand。提供同步/异步两种调用方式。
import copy from 'copy-to-clipboard';
// 同步(返回布尔值)
const success = copy('要复制的内容');
// 异步(返回 Promise)
const result = await copy('要复制的内容', { format: 'text/plain' });
react-copy-to-clipboard底层依赖 copy-to-clipboard,通过 React 组件封装。
import { CopyToClipboard } from 'react-copy-to-clipboard';
<CopyToClipboard text="要复制的内容" onCopy={() => alert('已复制')}>
<button>复制</button>
</CopyToClipboard>
vue-clipboard2底层依赖 clipboard 库,通过 Vue 指令封装。
// Vue 2 组件
import Vue from 'vue';
import VueClipboard from 'vue-clipboard2';
Vue.use(VueClipboard);
// 模板
<button v-clipboard:copy="'要复制的内容'" v-clipboard:success="onSuccess">复制</button>
clipboard 和 vue-clipboard2 采用声明式设计:在 HTML 中通过属性或指令定义复制内容,JS 只负责初始化。
<!-- clipboard -->
<button data-clipboard-text="固定文本">复制</button>
<!-- vue-clipboard2 -->
<button v-clipboard:copy="fixedText">复制</button>
局限:无法直接复制动态生成的文本(如用户输入、API 返回结果),除非重新渲染 DOM 或手动更新属性。
clipboard-polyfill、copy-to-clipboard 和 react-copy-to-clipboard 支持在任意 JS 逻辑中调用。
// 动态场景:复制当前时间
const now = new Date().toISOString();
// clipboard-polyfill
await writeText(now);
// copy-to-clipboard
copy(now);
// react-copy-to-clipboard(在事件处理器中)
const handleCopy = () => {
// 实际由组件内部调用 copy-to-clipboard
};
clipboard:仅支持 execCommand,在 iOS Safari 等环境中可能失效(因安全限制)。clipboard-polyfill:主动维护,持续适配新浏览器行为(如 Chrome 对 execCommand 的弃用警告),并提供 TypeScript 类型。copy-to-clipboard:覆盖 IE9+,但在严格 CSP 环境下可能因动态创建 DOM 而受阻。react-copy-to-clipboard:兼容性取决于底层 copy-to-clipboard。vue-clipboard2:兼容性取决于底层 clipboard,且因停止维护,未适配现代浏览器变更。⚠️ 注意:
vue-clipboard2的 GitHub 仓库已归档,明确标注 “Vue 2 only”,Vue 3 项目绝对不应使用。clipboard虽未正式废弃,但多年无更新,存在兼容性风险。
clipboard-polyfill 和 copy-to-clipboard(异步模式)通过 Promise 捕获错误,可区分“用户拒绝”和“技术失败”。
writeText(content).catch(e => {
if (e.name === 'NotAllowedError') {
// 用户未授予权限
}
});
clipboard 和 vue-clipboard2 通过事件回调提供成功/失败状态,但无法获取具体错误原因。
// clipboard
clipboard.on('error', e => console.log('复制失败'));
react-copy-to-clipboard 通过 onCopy 回调传递结果。
<CopyToClipboard onCopy={(text, result) => {
if (!result) alert('失败');
}} />
| 库 | 原生 JS | React | Vue 2 | Vue 3 |
|---|---|---|---|---|
clipboard | ✅ | ⚠️ 需手动集成 | ⚠️ 需手动集成 | ⚠️ 需手动集成 |
clipboard-polyfill | ✅ | ✅ | ✅ | ✅ |
copy-to-clipboard | ✅ | ✅ | ✅ | ✅ |
react-copy-to-clipboard | ❌ | ✅ | ❌ | ❌ |
vue-clipboard2 | ❌ | ❌ | ✅ | ❌ |
clipboard-polyfill — 现代 API 语义、主动维护、Promise 支持、类型安全。copy-to-clipboard — 无依赖、小体积、同步/异步双模式。react-copy-to-clipboard;否则直接用 copy-to-clipboard 或 clipboard-polyfill。vue-clipboard2 可用,但需注意其已停止维护;更推荐直接集成 copy-to-clipboard。clipboard(无维护)和 vue-clipboard2(Vue 3 不兼容且已归档)。无论选用哪个库,建议封装一层统一接口处理权限和降级:
// 使用 clipboard-polyfill
import { writeText } from 'clipboard-polyfill';
export async function safeCopy(text) {
try {
await writeText(text);
return { success: true };
} catch (err) {
// 处理用户拒绝或浏览器限制
if (err.name === 'NotAllowedError') {
return { success: false, reason: 'user-denied' };
}
return { success: false, reason: 'technical-error' };
}
}
// 在 React/Vue/原生中统一调用
const handleClick = async () => {
const result = await safeCopy(dynamicContent);
if (!result.success) {
// 显示友好提示
alert('请手动复制:' + dynamicContent);
}
};
剪贴板操作虽小,却直接影响用户体验。选择有维护、有降级、有错误反馈的方案,才能避免“复制按钮点不动”的尴尬。
选择 clipboard 如果你需要一个轻量、无依赖、基于 DOM 事件驱动的通用库,适用于原生 JavaScript 项目或任何框架。它通过 data-attribute 声明式绑定目标元素,适合静态内容复制场景,但不支持动态文本或程序化调用(除非手动触发事件)。注意:该库已不再积极维护,新项目应谨慎评估。
选择 clipboard-polyfill 如果你追求现代 Clipboard API 的语义一致性,并希望在旧浏览器中自动回退到兼容方案。它提供统一的 Promise 接口,支持文本和富文本(HTML)复制,且主动维护。适合需要程序化控制、类型安全和未来兼容性的项目。
选择 copy-to-clipboard 如果你需要最简化的函数式 API 来程序化复制任意字符串,且不关心声明式绑定。它体积小、无依赖、支持 Promise 和回调两种风格,兼容性覆盖 IE9+。适合 React、Vue 等框架中通过逻辑层直接调用的场景。
选择 react-copy-to-clipboard 如果你在 React 项目中需要将复制功能作为组件使用,并希望自动处理状态反馈(如是否已复制)。它封装了底层复制逻辑,提供 <CopyToClipboard> 组件,适合 JSX 声明式开发,但仅限 React 生态。
选择 vue-clipboard2 如果你在 Vue 2 项目中希望通过指令(如 v-clipboard)快速集成复制功能。它基于 clipboard 库封装,提供 Vue 指令和方法调用两种方式。注意:该库明确标注为 Vue 2 专用,且仓库已归档,Vue 3 项目不应使用。
Modern copy to clipboard. No Flash. Just 3kb gzipped.
Copying text to the clipboard shouldn't be hard. It shouldn't require dozens of steps to configure or hundreds of KBs to load. But most of all, it shouldn't depend on Flash or any bloated framework.
That's why clipboard.js exists.
You can get it on npm.
npm install clipboard --save
Or if you're not into package management, just download a ZIP file.
First, include the script located on the dist folder or load it from a third-party CDN provider.
<script src="dist/clipboard.min.js"></script>
Now, you need to instantiate it by passing a DOM selector, HTML element, or list of HTML elements.
new ClipboardJS('.btn');
Internally, we need to fetch all elements that matches with your selector and attach event listeners for each one. But guess what? If you have hundreds of matches, this operation can consume a lot of memory.
For this reason we use event delegation which replaces multiple event listeners with just a single listener. After all, #perfmatters.
We're living a declarative renaissance, that's why we decided to take advantage of HTML5 data attributes for better usability.
A pretty common use case is to copy content from another element. You can do that by adding a data-clipboard-target attribute in your trigger element.
The value you include on this attribute needs to match another's element selector.
<!-- Target -->
<input id="foo" value="https://github.com/zenorocha/clipboard.js.git" />
<!-- Trigger -->
<button class="btn" data-clipboard-target="#foo">
<img src="https://raw.githubusercontent.com/zenorocha/clipboard.js/HEAD/assets/clippy.svg" alt="Copy to clipboard" />
</button>
Additionally, you can define a data-clipboard-action attribute to specify if you want to either copy or cut content.
If you omit this attribute, copy will be used by default.
<!-- Target -->
<textarea id="bar">Mussum ipsum cacilds...</textarea>
<!-- Trigger -->
<button class="btn" data-clipboard-action="cut" data-clipboard-target="#bar">
Cut to clipboard
</button>
As you may expect, the cut action only works on <input> or <textarea> elements.
Truth is, you don't even need another element to copy its content from. You can just include a data-clipboard-text attribute in your trigger element.
<!-- Trigger -->
<button
class="btn"
data-clipboard-text="Just because you can doesn't mean you should — clipboard.js"
>
Copy to clipboard
</button>
There are cases where you'd like to show some user feedback or capture what has been selected after a copy/cut operation.
That's why we fire custom events such as success and error for you to listen and implement your custom logic.
var clipboard = new ClipboardJS('.btn');
clipboard.on('success', function (e) {
console.info('Action:', e.action);
console.info('Text:', e.text);
console.info('Trigger:', e.trigger);
e.clearSelection();
});
clipboard.on('error', function (e) {
console.error('Action:', e.action);
console.error('Trigger:', e.trigger);
});
For a live demonstration, go to this site and open your console.
Each application has different design needs, that's why clipboard.js does not include any CSS or built-in tooltip solution.
The tooltips you see on the demo site were built using GitHub's Primer. You may want to check that out if you're looking for a similar look and feel.
If you don't want to modify your HTML, there's a pretty handy imperative API for you to use. All you need to do is declare a function, do your thing, and return a value.
For instance, if you want to dynamically set a target, you'll need to return a Node.
new ClipboardJS('.btn', {
target: function (trigger) {
return trigger.nextElementSibling;
},
});
If you want to dynamically set a text, you'll return a String.
new ClipboardJS('.btn', {
text: function (trigger) {
return trigger.getAttribute('aria-label');
},
});
For use in Bootstrap Modals or with any other library that changes the focus you'll want to set the focused element as the container value.
new ClipboardJS('.btn', {
container: document.getElementById('modal'),
});
Also, if you are working with single page apps, you may want to manage the lifecycle of the DOM more precisely. Here's how you clean up the events and objects that we create.
var clipboard = new ClipboardJS('.btn');
clipboard.destroy();
This library relies on both Selection and execCommand APIs. The first one is supported by all browsers while the second one is supported in the following browsers.
![]() | ![]() | ![]() | ![]() | ![]() | ![]() |
|---|---|---|---|---|---|
| 42+ ✔ | 12+ ✔ | 41+ ✔ | 9+ ✔ | 29+ ✔ | 10+ ✔ |
The good news is that clipboard.js gracefully degrades if you need to support older browsers. All you have to do is show a tooltip saying Copied! when success event is called and Press Ctrl+C to copy when error event is called because the text is already selected.
You can also check if clipboard.js is supported or not by running ClipboardJS.isSupported(), that way you can hide copy/cut buttons from the UI.
A browser extension that adds a "copy to clipboard" button to every code block on GitHub, MDN, Gist, StackOverflow, StackExchange, npm, and even Medium.
Install for Chrome and Firefox.
MIT License © Zeno Rocha