news 2026/10/1 10:03:30

VueUse useClipboardItems 实战指南:基于 ClipboardItem 的响应式剪贴板操作

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
VueUse useClipboardItems 实战指南:基于 ClipboardItem 的响应式剪贴板操作
  • 前端

【免费下载链接】vueuse

Collection of essential Vue Composition Utilities for Vue 3

项目地址:https://gitcode.com/gh_mirrors/vu/vueuse
点击查看免费下载

在 Vue 3 应用中直接操作系统剪贴板往往要面对异步 API、权限门控和内容格式转换等繁琐细节。VueUse 提供的useClipboardItems正是面向浏览器原生 Clipboard API 的响应式封装:它允许你基于ClipboardItem读写任意格式的剪贴板内容,并自动维护content、copied等响应式状态,让"复制、读取、状态回显"的整套流程开箱即用。读完本文,你将掌握useClipboardItems的完整 API、与useClipboard的选型差异,以及它背后的源码实现原理,可以直接在项目中落地一个支持多种 MIME 类型的复制/粘贴组件。

什么是 useClipboardItems

useClipboardItems是 VueUse 核心包(packages/core)中面向浏览器 Clipboard API 的响应式工具函数,位于 packages/core/useClipboardItems/index.ts,并从packages/core/index.ts统一对外导出。

它的核心能力可以概括为三点:

  • 响应式读取与写入:把navigator.clipboard.read()与navigator.clipboard.write()封装成可复用的响应式状态;
  • 基于ClipboardItem的多格式支持:可以复制图片、富文本、HTML、文本等任何浏览器ClipboardItem支持的内容;
  • 权限友好:剪贴板内容的读写受 Permissions API 门控,没有用户授权时不允许读取或修改剪贴板内容。

与 useClipboard 的区别:文本专用 vs 格式通用

VueUse 同时提供了两个剪贴板工具函数:useClipboard 与useClipboardItems。二者的选型差异非常清晰:

  • useClipboard是"纯文本"函数:它只处理字符串,内部会构造{ 'text/plain': value }的ClipboardItem进行写入(见 packages/core/useClipboard/index.ts),并额外支持legacy选项,在剪贴板 API 不可用时回退到document.execCommand('copy');
  • useClipboardItems是ClipboardItem导向的函数:它直接接收并写入ClipboardItem实例,因此可以复制任何ClipboardItem支持的内容,包括图片、HTML、自定义 MIME 类型等。

简而言之:只需要复制/粘贴文本时用useClipboard;需要处理多格式内容时用useClipboardItems。

快速上手:一个完整的复制按钮

原文档给出了一份完整的 Vue 单文件组件示例,这里完整保留并补充注释,方便直接复制运行:

<script setup lang="ts"> import { useClipboardItems } from '@vueuse/core' const mime = 'text/plain' const source = ref([ new ClipboardItem({ [mime]: new Blob(['plain text'], { type: mime }), }) ]) const { content, copy, copied, isSupported } = useClipboardItems({ source }) </script> <template> <div v-if="isSupported"> <button @click="copy(source)"> <!-- 默认情况下,`copied` 会在 1.5 秒后自动重置 --> <span v-if="!copied">Copy</span> <span v-else>Copied!</span> </button> <p> Current copied: <code>{{ content || 'none' }}</code> </p> </div> <p v-else> Your browser does not support Clipboard API </p> </template>

要点说明:

  • 通过new ClipboardItem({ [mime]: new Blob([...], { type: mime }) })构造剪贴板条目,mime声明内容的媒体类型;
  • isSupported用于能力检测,在不支持 Clipboard API 的浏览器中渲染降级提示;
  • 传入source选项后,copy()可以不传参数直接使用该 source 进行复制;
  • copied是一个临时状态,默认 1.5 秒后自动恢复为false,很适合驱动按钮文案切换。

API 详解

useClipboardItems的完整类型定义在 packages/core/useClipboardItems/index.ts,下面逐项拆解。

选项(UseClipboardItemsOptions)

export interface UseClipboardItemsOptions<Source> extends ConfigurableNavigator { /** * 是否启用剪贴板读取 * * @default false */ read?: boolean /** * 复制数据源 */ source?: Source /** * 重置 `copied` ref 状态所需的毫秒数 * * @default 1500 */ copiedDuring?: number }
选项类型默认值作用
readbooleanfalse是否启用剪贴板读取。启用后会在用户执行copy/cut时自动更新content
sourceSource(一般为ClipboardItems)无复制数据源。传入后copy()可以不传参数,内部通过toValue(source)解析
navigatorNavigatorwindow.navigator自定义navigator实例,来自ConfigurableNavigator,可用于 iframe 或测试环境注入

返回值(UseClipboardItemsReturn)

export interface UseClipboardItemsReturn<Optional> extends Supportable { content: Readonly<Ref<ClipboardItems>> copied: Readonly<ShallowRef<boolean>> copy: Optional extends true ? (content?: ClipboardItems) => Promise<void> : (text: ClipboardItems) => Promise<void> read: () => void }
返回值类型说明
isSupportedComputedRef<boolean>当前环境是否支持 Clipboard API(继承自Supportable)
contentReadonly<Ref<ClipboardItems>>当前剪贴板内容(ClipboardItem[]),只读
copiedReadonly<ShallowRef<boolean>>复制成功标记,copiedDuring毫秒后自动重置
copy(content?: ClipboardItems) => Promise<void>执行复制(是否可省略参数取决于是否传入source)
read() => void手动触发一次剪贴板读取,更新content

重载签名与Optional泛型

源码通过两个重载区分"是否配置了 source":

export function useClipboardItems(options?: UseClipboardItemsOptions<undefined>): UseClipboardItemsReturn<false> export function useClipboardItems(options: UseClipboardItemsOptions<MaybeRefOrGetter<ClipboardItems>>): UseClipboardItemsReturn<true>
  • 不传source时,copy必须显式传入ClipboardItems(Optional = false);
  • 传入source(可以是值、ref或 getter,即MaybeRefOrGetter<ClipboardItems>)时,copy可以省略参数(Optional = true),内部自动解析 source。

源码级原理解析

useClipboardItems的实现非常精简(packages/core/useClipboardItems/index.ts),却完整覆盖了能力检测、写入、读取、状态复位四条链路,下面逐一展开。

1. 能力检测:isSupported

const isSupported = useSupported(() => (navigator && 'clipboard' in navigator))

useSupported是 VueUse 通用的能力检测工具(packages/core/useSupported/index.ts),它基于useMounted的挂载状态构造一个computed,仅在组件挂载后计算Boolean(callback())。这意味着isSupported在 SSR 环境下为false——因为defaultNavigator = isClient ? window.navigator : undefined(见 packages/core/_configurable.ts),服务端没有navigator。这也是模板中用v-if="isSupported"做降级渲染的原因。

2. 写入链路:copy

async function copy(value = toValue(source)) { if (isSupported.value && value != null) { await navigator!.clipboard.write(value) content.value = value copied.value = true timeout.start() } }
  • value默认取toValue(source),因此source可以是普通数组、ref或 getter,写入时统一解包;
  • 通过navigator.clipboard.write(value)异步写入(参数是ClipboardItem[]);
  • 写入成功后同步更新content与copied,并启动复位计时器。

3. copied 自动复位:useTimeoutFn

const timeout = useTimeoutFn(() => copied.value = false, copiedDuring, { immediate: false })

useTimeoutFn(packages/shared/useTimeoutFn/index.ts)是对setTimeout的控制封装,{ immediate: false }表示创建时并不启动计时,而是由copy()在成功后调用timeout.start()。于是copied默认保持 1.5 秒后自动归零,无需手动清理。

4. 读取链路:updateContent 与事件监听

function updateContent() { if (isSupported.value) { navigator!.clipboard.read().then((items) => { content.value = items }) } } if (isSupported.value && read) { useEventListener(['copy', 'cut'], updateContent, { passive: true }) }
  • read()返回值就是updateContent,调用它会触发一次navigator.clipboard.read(),把读到的ClipboardItem[]写入content;
  • 当选项read: true时,函数还会通过useEventListener监听copy与cut事件(passive: true),用户在系统层面执行复制/剪切操作后自动刷新content;
  • 注意读取同样受权限门控,未授权时clipboard.read()会失败。

5. 状态实现:shallowRef 与 shallowReadonly

const content = shallowRef<ClipboardItems>([]) const copied = shallowRef(false) // ... return { isSupported, content: shallowReadonly(content), copied: shallowReadonly(copied), copy, read: updateContent, }

content与copied均采用shallowRef存储,对外通过shallowReadonly暴露只读视图,防止调用方直接篡改内部状态,保证状态流转始终经由copy/read两个入口。

读取剪贴板内容:结合 usePermission 与 getType

仓库自带的演示组件 packages/core/useClipboardItems/demo.vue 展示了读取方向的完整实践,核心思路是:用usePermission观察剪贴板读写权限,再对content中的每个ClipboardItem调用getType('text/plain')提取 Blob 文本:

import { useClipboardItems, usePermission } from '@vueuse/core' import { effect, shallowRef } from 'vue' const { content, isSupported, copy, read } = useClipboardItems() const permissionRead = usePermission('clipboard-read') const permissionWrite = usePermission('clipboard-write') effect(() => { Promise.all(content.value.map(item => item.getType('text/plain'))) .then(async (blobs) => { computedMimeType.value = blobs.map(blob => blob.type).join(', ') computedText.value = (await Promise.all(blobs.map(blob => blob.text()))).join(', ') }) })

在模板中,它同时渲染出clipboard-read与clipboard-write两个权限状态(prompt/granted/denied),并提供"Copy"与"Manual read"两个按钮分别触发copy([...])和read():

<button @click="() => copy([createClipboardItems(input)])">Copy</button> <button @click="() => read()">Manual read</button>

这套组合是读取方向的标准范式:usePermission('clipboard-read')负责权限提示与状态展示,read()负责拉取内容,item.getType(mime)负责按 MIME 提取对应数据。

注意事项

  • 权限门控:剪贴板内容访问受 Permissions API 约束,读取或修改前需要用户授权(浏览器的权限提示),授权状态可用usePermission(packages/core/usePermission)观察;
  • SSR 安全:由于defaultNavigator仅在客户端存在(isClient ? window.navigator : undefined),服务端渲染时isSupported恒为false,务必用v-if="isSupported"或客户端挂载后执行相关逻辑;
  • 降级分支:模板中的v-else分支用于不支持 Clipboard API 的浏览器,此时应提供替代方案(如需文本复制可考虑useClipboard的legacy回退);
  • 读取需要read: true:默认不监听剪贴板事件,如需在用户复制/剪切后自动同步content,需显式开启read: true选项;
  • content是ClipboardItem[]:直接渲染到页面通常需要像 demo 那样通过getType()提取并解析为文本或图片 URL 后再展示。

小结

useClipboardItems以极小的实现成本(核心仅约 40 行逻辑)把浏览器 Clipboard API 的读写、权限、事件监听与响应式状态管理整合为一个声明式接口:source+copy()解决写入,read+content解决读取,copiedDuring自动管理复制反馈,isSupported兜底能力降级。对于需要复制图片、富文本或自定义 MIME 内容的 Vue 3 应用,它是比useClipboard更贴合ClipboardItem能力的方案。

  • 前端

【免费下载链接】vueuse

Collection of essential Vue Composition Utilities for Vue 3

项目地址:https://gitcode.com/gh_mirrors/vu/vueuse
点击查看免费下载
上一篇:mousetrap 源码解读:用一行 API 识别"在资源管理器里双击启动"的 Windows CLI 进程
下一篇:基于 AWS SDK for C++ 的 Hello Rekognition 入门示例:编译、运行与 ListCollections 调用解析

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/1 10:01:28

产生式系统实战:用Python构建可追溯的规则推理引擎

1. 什么是产生式系统&#xff1f;它不是“AI黑箱”&#xff0c;而是可追溯、可调试的逻辑骨架你可能在AI课程里第一次听到“产生式系统”这个词时&#xff0c;脑子里浮现的是一个模糊的、带箭头的流程图&#xff0c;或者一段写着“IF...THEN...”的伪代码。但说实话&#xff0c…

作者头像 李华
网站建设 2026/10/1 10:00:41

交通目标检测YOLO数据集质量验证与增强实战

简介&#xff1a;本资源是一套面向计算机视觉初学者与YOLO目标检测实践者的交通场景专用数据集&#xff0c;聚焦道路环境中汽车、警告标志、红色交通灯等11类关键目标的识别与定位任务&#xff0c;适用于模型训练、算法验证及课程设计。压缩包共2000个文件&#xff0c;含1420个…

作者头像 李华