# 一个可以复制文本到粘贴板的hook

ChenHaoJie9527
Table of Contents

在前端开发中,通常会有一些Copy的按钮,需要支持复制粘贴的功能。现代浏览器复制粘贴的核心是navigator.clipboard.writeText(value),一些老旧浏览器可能没办法使用这个API,但是可以使用document.execCommand("copy")解决方案。

函数实现

function useCopy() {
const [copied, setCopied] = useState(null)
const copyHandle = () => {
try {
// 优先使用现代 Clipboard API
if (navigator?.clipboard?.writeText) {
await navigator.clipboard.writeText(value)
setCopied(value)
} else {
throw new Error('writeText not supported')
}
} catch (error) {
oldSchoolCopy(value)
setCopied(value)
}
}
const resetHandle = useCallback(() => {
setCoped(null)
}, [])
return {
copied,
copyHandle,
resetHandle,
}
}
// 传统复制方案
function oldSchoolCopy(text) {
try {
const tempTextArea = document.createElement('textarea')
tempTextArea.value = text
tempTextArea.style.position = 'fixed'
tempTextArea.style.left = '-999999px'
tempTextArea.style.top = '-999999px'
document.body.appendChild(tempTextArea)
tempTextArea.focus()
tempTextArea.select()
document.execCommand('copy')
document.body.removeChild(tempTextArea)
} catch (error) {
console.error('Old school copy failed:', error)
}
}

Demo

使用示例

基础用法

import useCopy from "../hooks/useCopy";
const CopyDemo = () => {
const { copied, copyHandle, resetHandle } = useCopy();
return (
<div className="p-4 border border-foreground/20 rounded-lg bg-background/50 dark:bg-background/30 backdrop-blur-sm">
<div className="flex gap-3 items-center">
<button
type="button"
onClick={() => copyHandle("Hello, world!")}
className="inline-flex text-accent border-3 border-accent/30 border-double py-1.5 px-3 whitespace-nowrap hover:bg-accent/10 rounded-xl transition-colors font-semibold"
>
{copied ? "已复制!" : "复制文本"}
</button>
{copied && (
<button
type="button"
onClick={resetHandle}
className="inline-flex text-foreground/70 border-3 border-foreground/20 border-double py-1.5 px-3 whitespace-nowrap hover:bg-foreground/10 rounded-xl transition-colors font-semibold"
>
重置
</button>
)}
</div>
{copied && (
<div className="mt-3 p-2 rounded text-sm text-accent border-accent/30 bg-accent/10">
✅ 文本已复制到剪贴板
</div>
)}
</div>
);
};
export default CopyDemo;

复制代码块

function CopyCodeBlock() {
const { copied, copyHandle } = useCopy()
const code = `function hello() {
console.log('Hello, World!')
}`
return (
<div className="relative">
<pre className="bg-gray-100 p-4 rounded">
<code>{code}</code>
</pre>
<button
onClick={() => copyHandle(code)}
className="absolute top-2 right-2 px-2 py-1 text-sm bg-gray-200 rounded hover:bg-gray-300"
>
{copied ? '' : '复制'}
</button>
</div>
)
}

带反馈的复制按钮

function CopyWithFeedback() {
const { copied, copy } = useCopy()
const [isLoading, setIsLoading] = useState(false)
const handleCopy = async (text: string) => {
setIsLoading(true)
try {
const success = await copy(text)
if (success) {
// 显示成功提示
setTimeout(() => {
// 可以在这里添加 toast 通知
}, 100)
}
} finally {
setIsLoading(false)
}
}
return (
<button
onClick={() => handleCopy('要复制的文本')}
disabled={isLoading}
className="px-4 py-2 bg-green-500 text-white rounded disabled:opacity-50"
>
{isLoading ? '复制中...' : copied ? '已复制!' : '复制'}
</button>
)
}

兼容性分析

现代浏览器支持

浏览器Clipboard API 支持最低版本
Chrome 支持66+
Firefox 支持63+
Safari 支持13.1+
Edge 支持79+
Opera 支持53+

传统方法兼容性

document.execCommand('copy') 方法在以下浏览器中支持:

浏览器支持版本
Chrome43+
Firefox41+
Safari9+
IE10+
Edge12+

降级策略

采用了优雅降级的策略:

  1. 优先使用现代 API:首先尝试使用 navigator.clipboard.writeText()
  2. 降级到传统方法:如果不支持,则使用 document.execCommand('copy')
  3. 错误处理:两种方法都失败时,返回 false 并记录错误

安全考虑

  • HTTPS 要求:现代 Clipboard API 只在 HTTPS 环境下工作
  • 用户交互:某些浏览器要求复制操作必须由用户交互触发
  • 权限:某些浏览器可能需要用户授权剪贴板访问权限
My avatar

Thanks for reading my blog post! Feel free to check out my other posts or contact me via the social links in the footer.


相关文章

评论