Skip to content
霞露小伙 — HfWang
On this page

浏览器复制功能实现

WARNING

概要内容概要

参考文章/博客:

背景

工作项目为“订单管理系统(oms)”,技术栈(vue2 + iView3)项目中存在大量需要通过点击实现复制文本信息的需求(包括复制订单号、快递单号、退款单号、用户信息、发票信息、店铺信息和平台信息等各种各样的信息),使用场景包括直接复制文本信息和复制输入框中的信息。

原有的解决方案

复制函数:

此函数会被绑定到 Vue 的实例上作为全局可用的方法

js
// utils/copyData.js

/**
 * @params _data 要复制的信息
 * @params message 复制成功提示信息
 * @params instance vue 实例
 */
function copyData(_data, message instance){
  iftypeof _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}`);
}

原理:

创建一个 textareainput 标签,将要复制的数据作为标签的 value, 利用 textareainput 标签的 select 方法自动选中标签,在使用 document.execCommand 这个 api 自动复制被 select 选中的标签中的文本信息,最后移除创建的标签即可。

使用:

html
<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 系统上,textareainput 无法使用 select 命令。并且 execCommand 这个 api 是已经废弃的,谁知道官方什么时候就把这个给移除了。

替代方案

使用 navigator.clipboard

navigator.clipboard:

方法作用支持浏览器
read从剪贴板读取数据(比如图片),返回一个 Promise 对象。all
readText从操作系统读取文本;返回一个 Promiseall
write写入任意数据至操作系统剪贴板 ;返回一个 PromiseChrome、Safari
writeText写入文本至操作系统剪贴板。返回一个 Promise,Chrome、fireFox、Safari

使用示例:

  1. writeText: 用于将文本内容写入剪贴板

    js
    async function writeDataToClipboard() {
      const result = await navigator.clipboard.writeText('Hello')
      console.log(result)
    }
  2. 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)
    }
  3. readText:用于复制剪贴板里面的文本数据

    js
    ;async (e) => {
      const text = await navigator.clipboard.readText()
      console.log(text)
    }
  4. read: 用于复制剪贴板里面的数据,可以是文本数据,也可以是二进制数据(比如图片),该方法需要用户明确给予许可

    js
    async 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

shell
pnpm add vue-clipboard3

使用:

vue
<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

shell
pnpm add react-clipboard.js
jsx
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 通用的

shell
pnpm add copy-to-clipboard
js
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 作为复制的工具函数。

本站中引用到的其他资料,如有侵权,请联系本人删除