浏览器复制功能实现
WARNING
概要内容概要
参考文章/博客:
背景
工作项目为“订单管理系统(oms)”,技术栈(vue2 + iView3)项目中存在大量需要通过点击实现复制文本信息的需求(包括复制订单号、快递单号、退款单号、用户信息、发票信息、店铺信息和平台信息等各种各样的信息),使用场景包括直接复制文本信息和复制输入框中的信息。
原有的解决方案
复制函数:
此函数会被绑定到 Vue 的实例上作为全局可用的方法
// utils/copyData.js
/**
* @params _data 要复制的信息
* @params message 复制成功提示信息
* @params instance vue 实例
*/
function copyData(_data, message instance){
if (typeof _data !== 'string') {
instance.$Message.error('只能复制字符串信息');
return;
}
let text = document.createElement("textarea");
text.innerHTML = data;
document.body.appendChild(text);
text.select();
// MDN 显示该 api 已废弃
document.execCommand("copy");
document.body.removeChild(text);
// 复制成功提示
const msg = message ? message : "复制成功:";
instance.$Message.success(`${msg} ${text.value}`);
}
原理:
创建一个 textarea
或 input
标签,将要复制的数据作为标签的 value
, 利用 textarea
、input
标签的 select
方法自动选中标签,在使用 document.execCommand
这个 api 自动复制被 select
选中的标签中的文本信息,最后移除创建的标签即可。
使用:
<template>
<div>
<span @click="copyInfo(orderId)">{{ orderId }}</span>
</div>
</template>
<script>
export default {
data: () => {
orderId: '123456789', // 假装它是订单号
},
methods: {
copyInfo(data) {
const msg = `成功复制订单号: ${data}`;
this.copyData(data, msg, this);
}
}
}
</script>
不足
在 IOS 系统上,textarea
或 input
无法使用 select
命令。并且 execCommand
这个 api 是已经废弃的,谁知道官方什么时候就把这个给移除了。
替代方案
navigator.clipboard
:
方法 | 作用 | 支持浏览器 |
---|---|---|
read | 从剪贴板读取数据(比如图片),返回一个 Promise 对象。 | all |
readText | 从操作系统读取文本;返回一个 Promise | all |
write | 写入任意数据至操作系统剪贴板 ;返回一个 Promise | Chrome、Safari |
writeText | 写入文本至操作系统剪贴板。返回一个 Promise, | Chrome、fireFox、Safari |
使用示例:
writeText: 用于将文本内容写入剪贴板
jsasync function writeDataToClipboard() { const result = await navigator.clipboard.writeText('Hello') console.log(result) }
write: 用于将任意数据写入剪贴板,可以是文本数据,也可以是二进制数据
js// demo1 async function copyImage() { const input = document.getElementById('file') const blob = new Blob(['sample 2'], { type: 'text/plain' }) const clipboardItem = new ClipboardItem({ 'text/rt': blob, 'image/png': input.files[0], }) const response = await navigator.clipboard.write([clipboardItem]) console.log(response) } // demo2, 一下方式目前仅 Safari 支持 async function copyImage2() { const fetchImage = () => { const input = document.getElementById('file') return Promise.resolve(input.files[0]) } const clipboardItem = new ClipboardItem({ ['image/png']: fetchImage(), }) const response = await navigator.clipboard.write([clipboardItem]) console.log(response) }
readText:用于复制剪贴板里面的文本数据
js;async (e) => { const text = await navigator.clipboard.readText() console.log(text) }
read: 用于复制剪贴板里面的数据,可以是文本数据,也可以是二进制数据(比如图片),
该方法需要用户明确给予许可
jsasync function getClipboardContents() { try { const clipboardItems = await navigator.clipboard.read() for (const clipboardItem of clipboardItems) { for (const type of clipboardItem.types) { const blob = await clipboardItem.getType(type) console.log(URL.createObjectURL(blob)) } } } catch (err) { console.error(err.name, err.message) } }
接下来来看一些比较常见的库(都是封装 navigator.clipboard 的)
vue-clipboard
pnpm add vue-clipboard3
使用:
<template lang="html">
<div>
<input type="text" v-model="text">
<button @click="copy">Copy!</button>
</div>
</template>
<script lang="ts">
import { defineComponent, ref } from '@vue/composition-api'
import useClipboard from 'vue-clipboard3'
export default defineComponent({
setup() {
const { toClipboard } = useClipboard()
const text = ref('')
const copy = async () => {
try {
await toClipboard(text.value)
console.log('Copied to clipboard')
} catch (e) {
console.error(e)
}
}
return { copy, text }
}
})
</script>
react-clipboard
pnpm add react-clipboard.js
import ReactDOM from 'react-dom';
import React, { Component } from 'react';
import Clipboard from 'react-clipboard.js';
class MyView extends Component {
render() {
return (
<Clipboard data-clipboard-text="I'll be copied">
copy to clipboard
</Clipboard>
);
}
}
ReactDOM.render(<MyView/>, document.getElementById('app'));
react 和 vue 通用的
pnpm add copy-to-clipboard
import copy from 'copy-to-clipboard';
copy('Text');
// Copy with options
copy('Text', {
debug: true,
message: 'Press #{key} to copy',
});
总结
由于项目中仅存在复制文本信息的场景,所以使用 navigator.clipboard.writeText
这个方法就可以满足业务需求了,但由于这个 api 目前并不是所有浏览器都支持(客户大部分使用 Chrome,但不排除还有使用 IE 的),所以目前仅对小部分场景使用了新的 api 作为复制的工具函数。