当CV工程师碰到了拷贝粘贴的需求——useClipboard的使用及源码解析
日常开发中我们有时会遇到拷贝粘贴的功能,笔者还记得曾经使用过react-copy-to-clipboard 和 vue-clipboard2。最近在看vueuse的时候发现了useClipboard就简单研究了一下,日后vue3项目中如果有使用场景可以用一下。
1.原生Clipboard
在看useClipboard源码之前,需要一些前置知识,那就是原生Clipboard的API。首先通过一张图概览一下浏览器原生Clipboard的相关知识:
对上图涉及的知识逐一解释:
- 剪贴板 Clipboard API 提供了剪贴板命令与异步读写系统剪贴板的能力。
- 从权限 Permissions API 获取权限之后,才能访问剪贴板内容。
- Clipboard API 包括异步剪贴板 API(AsyncClipboard API)和 剪贴板事件 API(Clipboard Event API)。
- Clipboard读取剪切板有两个方法read()和readText()分别用于读取数据(比如图片)和文本;写入剪切板有两个方法write()和writeText()分别用于将任意数据写入和将文本写入。
- ClipboardEvent接口描述了与修改剪切板相关信息的事件,包括剪切,复制和粘贴。
更多关于Clipboard API的细节以及兼容性问题您可以访问MDN文档详细学习。看一段示例代码:
navigator.clipboard.readText().then(
clipText => document.querySelector(".editor").innerText += clipText);
2.useClipboard
2.1简介
useClipboard是响应式的剪贴板 API。提供响应剪贴板命令(剪切、复制和粘贴)以及异步读取和写入系统剪贴板的能力。访问剪贴板内容需要获得Permission API的相关权限,未经用户许可则不允许读取或更改剪贴板内容。
2.2例子
官方文档的示例代码如下所示:
<script setup lang="ts">
import { ref } from 'vue'
import { useClipboard, usePermission } from '@vueuse/core'
const input = ref('')
const { text, isSupported, copy } = useClipboard()
const permissionRead = usePermission('clipboard-read')
const permissionWrite = usePermission('clipboard-write')
</script>
<template>
<div v-if="isSupported">
<note>
Clipboard Permission: read <b>{{ permissionRead }}</b> | write
<b>{{ permissionWrite }}</b>
</note>
<p>
Current copied: <code>{{ text || 'none' }}</code>
</p>
<input v-model="input" type="text">
<button @click="copy(input)">
Copy
</button>
</div>
<p v-else>
Your browser does not support Clipboard API
</p>
</template>
首先引入 useClipboard,从useClipboard中解构出text, isSupported, copy。text是当前从剪切板读取到的文本,isSupported用于判断当前浏览器是否支持剪切板API,copy是将文本写入到剪切板的方法。
usePermission用于获取权限,使用其查看了clipboard-read和clipboard-write的权限,并将权限展示到页面上。通过下图可以看到读取的权限是prompt也就是询问,而写入操作被授权了即granted。
定义了响应式的变量input绑定到input标签上,当用户点击按钮时则调用copy()方法将input的内容写入剪切板。
初始时我们没有向剪切板写入内容,text是空字符串,所以页面显示当前拷贝内容为none:
当用户输入'123'点击copy按钮后,text的值也就是从剪切板读取到的文本变成了'123',所以页面展示内容也变成了'123'
2.3源码
这里我们只保留了核心逻辑的40多行代码,您可以查看源码访问全部代码。
export function useClipboard(options: ClipboardOptions<MaybeRef<string> | undefined> = {}): ClipboardReturn<boolean> {
const {
navigator = defaultNavigator,
read = false,
source,
copiedDuring = 1500,
} = options
const events = ['copy', 'cut']
const isSupported = Boolean(navigator && 'clipboard' in navigator)
const text = ref('') // 与剪切板内容相对应的响应式值
const copied = ref(false) // 是否拷贝完成
const timeout = useTimeoutFn(() => copied.value = false, copiedDuring)
// 更新text
function updateText() {
navigator!.clipboard.readText().then((value) => {
text.value = value
})
}
// 监听拷贝和剪切事件
if (isSupported && read) {
for (const event of events)
useEventListener(event as WindowEventName, updateText)
}
// 将响应式值value拷贝到text
async function copy(value = unref(source)) {
if (isSupported && value != null) {
await navigator!.clipboard.writeText(value)
text.value = value
copied.value = true
timeout.start()
}
}
return {
isSupported,
text: text as ComputedRef<string>,
copied: copied as ComputedRef<boolean>,
copy,
}
}
2.3.1参数
(1)navigator默认为window.navigator, 其定义如下:
export const defaultNavigator = /* #__PURE__ */ isClient ? window.navigator : undefined
(2)read表示是否允许读取剪切板的内容,默认值是false, 也就是默认不实时读取剪切板的内容。我们通过例子的截图知道默认clipboard-read的值是prompt即询问,您可以通过浏览器的隐私设置来设置对于剪切板的权限:
当调用useClipboard时指定了read为true, 并且你允许查看赋值到剪切板中的数据,则此时只要剪切板里有内容就会实时显示:
const { text, isSupported, copy } = useClipboard({read:true})
上图是选中了Clipboard这几个字后右键点击复制后系统弹出确认框。
点击允许后赋值到剪切板的内容实时赋值给了text并显示在页面中了。
(3)source拷贝的源数据,可选的。source的原始值可以作为copy函数的默认值:
async function copy(value = unref(source)) {//省略}
如下图所示当没有指定source时的情况:
可以看到value对应传给copy的参数input, 而source是undefined。
(4)copiedDuring为重置copied的毫秒数,copied用来表示是否拷贝完成。
2.3.2updateText
function updateText() {
navigator!.clipboard.readText().then((value) => {
text.value = value
})
}
updateText用于更新text的值,从剪切板中读取数据然后更新text,读取剪切板数据使用的是readText()方法。
2.3.3监听拷贝和剪切
if (isSupported && read) {
for (const event of events)
useEventListener(event as WindowEventName, updateText)
}
在允许读取剪切板的情况下,如果发生拷贝和剪切则用剪切板中的内容更新text。
2.3.4 copy方法
async function copy(value = unref(source)) {
if (isSupported && value != null) {
await navigator!.clipboard.writeText(value)
text.value = value
copied.value = true
timeout.start()
}
}
copy方法用于将参数value写入剪切板并赋值给text,写入剪切板使用的是writeText()方法。至此,useClipbord的源码就分析完了,挺简单的但是读完也有收获。
3.总结
本文介绍了原生的Clipboard API之后又介绍了useClipboard 的使用,然后结合示例代码的调试过程分析了useClipboard 的源码。Clipboard API中的readText()和 writeText()是useClipboard 的灵魂。
转载自:https://juejin.cn/post/7109692811691327525