# 一个可以复制文本到粘贴板的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')
方法在以下浏览器中支持:
浏览器 | 支持版本 |
---|---|
Chrome | 43+ |
Firefox | 41+ |
Safari | 9+ |
IE | 10+ |
Edge | 12+ |
降级策略
采用了优雅降级的策略:
- 优先使用现代 API:首先尝试使用
navigator.clipboard.writeText()
- 降级到传统方法:如果不支持,则使用
document.execCommand('copy')
- 错误处理:两种方法都失败时,返回
false
并记录错误
安全考虑
- HTTPS 要求:现代 Clipboard API 只在 HTTPS 环境下工作
- 用户交互:某些浏览器要求复制操作必须由用户交互触发
- 权限:某些浏览器可能需要用户授权剪贴板访问权限