前端复制方案全覆盖!验证真机与生产代码完美一致!✨

前言

网上讲粘贴复制的很多,讲清楚复制异步数据得很少,在真机上真正验证过得凤毛麟角,正巧工作上遇到了复制接口返回的数据这个问题,求助了很多人,没有太好的解决办法,最终通过修改交互实现了这个复制功能,故写篇文档记录一下,也分享给大家。

主流复制方案

原生js API实现

document.execCommand

概述

document暴露 execCommand 方法

该方法允许运行命令来操纵可编辑内容区域的元素

我们在使用时,常常通过以一个不可见的input 或者textrea,获取value值

用document.exexCommand('copy')复制进粘贴板

缺点

MDN已经提示这个API,已经废弃

新版本浏览器兼容性尚不可知,基于高可用的原则,现在并不推荐在开发中使用。

但是,如果需要复制的是非常大段的内容,则 execCommand() 方法可能会引起卡顿,因为 execCommand() 方法是一个同步方法,必须等复制操作结束,才能继续执行后面的代码。

为了兼容移动端各个浏览器,传统的select() 在移动端会失效

需要做兼容处理,处理代码比较恶心,在开发中也不建议使用,下面我发一个我们在生产中使用的版本,供大家参考

兼容移动端代码

下面这段代码已在各个浏览器,各个手机主流机型验证过,经得住时间的考验

js function copy(text) { let input = document.createElement('input'); input.value = text; input.readOnly = true; document.body.appendChild(input); input.select(); let result = document.execCommand('copy'); if (result) { setTimeout(()=>{document.body.removeChild(input);input=null},0) return true; } else { const range = document.createRange(); range.selectNode(input); const selection = window.getSelection(); if (selection.rangeCount > 0) { selection.removeAllRanges(); selection.addRange(range); document.execCommand('copy'); setTimeout(() => {document.body.removeChild(input);}, 0) return true; } } return false; }

引入第三方库 基于clipboard.js实现

概述

行业内最成熟的库就是clipboard.js。

在这个基础上做了简单封装的vue-clipboard2,vue-clipboard3。

底层库一样,都是换汤不换药。

安装方式

二选一即可

``` npm install clipboard --save

### 优点 第三方库,内部针对各个浏览器都做了兼容性处理,可用性更高,且在不断更新,这个在ios 安卓设备无明显兼容性问题 ### 代码展示 ```js var clipboard = new ClipboardJS('.btn',{ text: () => 'xxx', }); clipboard.on('success', function (e) { console.log(e); //打印动作信息(copy或者cut) console.info('Action:', e.action); //打印复制的文本 console.info('Text:', e.text); //打印trigger console.info('Trigger:', e.trigger); clipboard.destroy() }); clipboard.on('error',function(e){ }); ``` ## Clipboard API execCommand替代方案Clipboard ### 概述 剪贴板 Clipboard API 提供了响应剪贴板命令(剪切、复制和粘贴)与异步读写系统剪贴板的能力。 从权限 Permissions API 获取权限之后,才能访问剪贴板内容; 如果用户没有授予权限,则不允许读取或更改剪贴板内容。 **该 API 被设计用来取代使用 document.execCommand() 的剪贴板访问方式。** ### 优点 新的API,调用简单,兼容性问题少 基于Promise,不用像execCommand一样还得选中范围 看了一下兼容性也挺不错的 ### 兼容性分析 兼容性比较低,在 [can I use](https://github.com/zenorocha/clipboard.js)上查了一下 **ios系统需要13.1以上,安卓系统需要6以上已能支持91.59%的用户** ![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/7492efadf0234628bc66e979a07630f0~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=2790&h=800&s=863122&e=png&b=eee7d4) ### 使用注意点 出于安全策略限制,只能在https域名和本地域名下使用。 在http下和非本地域名下 执行navigator.clipboard返回undefined ### 代码演示 ```js navigator.clipboard.writeText(value); navigator.clipboard.writeText(value).then(() => {}); ``` # 异步数据如何复制 ## 业务场景 场景是这样,用户点击按钮,去调用接口,把接口返回的内容复制到粘贴板上。 我天真的使用了之前已经在成熟的方案一方案二,结果被测试啪啪打脸。 **重要事情说三遍** document.execCommand,clipboard.js均不支持异步数据的复制 document.execCommand,clipboard.js均不支持异步数据的复制 document.execCommand,clipboard.js均不支持异步数据的复制 ## 遇到的问题 真机上的表现 document.execCommand android 可以复制成功,ios 复制不生效 clipboard.js android ios 均需要点击两次才能完成复制 ## 网友们的方案 **方案一:** 建立两个dom,一个dom1执行获取数据操作,一个dom2执行复制操作,点击dom1获取数据之后,默认去触发dom2的复制事件。 真机测试,无法粘贴,需要点击2次。才能复制。 **方案二:** 利用async await 将代码改写成同步代码,当时看到这个方案,就觉得不靠谱,属于自自欺人,实际还是验证了下,确实不行,真机测试,无法粘贴,需要点击2次。才能复制。 ## 根本原因 通过大量调研:总结出一句话 ****复制操作之前如果调用接口,浏览器出于安全策略,不会执行复制操作**** 之后的demo也验证了我的结论,如果复制之前执行setTimeout再复制数据无任何问题。 复制之前调用接口,再复制接口返回数据,就会出现复制失效。 再次点击按钮,发现执行了两次复制操作,可见我们注册复制事件已经成功了。 从程序执行角度来说,代码是没有问题的,只是复制操作被拦截了,各个浏览器表现不一致。 ## 解决方案 ***修改交互*** 将异步数据需要调用的接口,提前调用,在点击复制按钮之前,直接使用已经获得的数据。 ***使用Clipboard API*** # 技术调研 通过解决这个bug,发现出几个问题 - 前端领域,网络上的博客普遍质量不高,讲原理的多于讲实践的,生搬硬套得多于写原创的。 - 求助网络,不如求助网友,虽然网友提供的思路没有采纳,但是给了很大的支持。 因此出于这个原因,我调研了前端三种主流复制的方案,并自己做了验证。 ## 三种方案在真机上表现 ![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/a0367777437f4db1bdcc44716df4824b~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=1582&h=298&s=171021&e=png&b=fdfdfd) ![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/8d4b17dcae07458e877b7209f58fab92~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=1440&h=3200&s=592395&e=png&b=ffffff) ## 三种技术方案对比 ![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/a1b975e50911492da082513c1d071277~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=1018&h=420&s=42555&e=png&b=fcfcfc) ## 复制权限控制 **苹果对剪切板的权限实际上没有作任何控制**,这意味着任何应用都是无限制的读取剪切板内容不需要用户的授权 **主流安卓机器浏览器**,复制之前都需要判断浏览器是否赋予写入剪切板权限,读取剪切板权限。 与我们复制功能强相关的权限就是写入剪切板权限 ![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/e25ad36734f84993a1a08134a1556972~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=1152&h=2560&s=591479&e=png&b=fefefe) ## 权限种类 一般权限种类有 - 拒绝 - 询问 - 仅在使用中允许 - 始终允许 以qq浏览器为例 ![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/e53a299580f243ababfc6b5bb35100bd~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=1152&h=2560&s=321976&e=png&b=d9d9d9) - 当用户选择拒绝,所有复制API全部失效 - 当用户选择询问,会自动拉起询问弹窗,是否开启写入粘贴板权限 - 当用户选择仅在使用中允许和始终允许,则之后复制功能正常,不会询问 所以需要我们在调用复制代码之前考虑增加权限判断 ## 如何获取权限 以google浏览器为例,可以先查权限 权限的值为 - granted 允许 - denied 拒绝 - prompt 询问 ```js navigator.permissions.query({ name: 'clipboard-read' }).then(permissionStatus => { // permissionStatus.state 的值是 'granted'、'denied'、'prompt': console.log(permissionStatus.state); }); navigator.permissions.query({ name: 'clipboard-write' }).then(permissionStatus => { // permissionStatus.state 的值是 'granted'、'denied'、'prompt': console.log(permissionStatus.state); }); ``` ## 兼容性 permissions.query 的兼容性 ![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/8d9acba8abde4bd68450f8cde1dbbcc6~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=2766&h=886&s=863298&e=png&b=ece3cf) 可以看出兼容性非常不好,谷歌43以上都支持,safari全不支持,安卓浏览器不支持,部门安卓浏览器权限支持不明确 加上这是google浏览器自定义的标准,目前属于一个实验性属性,业内还没有形成一个统一的标准,建议慎重使用 # 总结 ## 前端究竟如何处理复制功能 1.如果在app内页面,可推动app提供复制内容的方法,前端直接去调用 2.修改交互。将异步数据需要调用的接口,提前调用,在点击复制按钮之前,直接使用已经获得的数据。 或者在按钮之上,再增加弹窗,提示用户复制,在用户点击弹窗确认再执行复制,从交互上分离复制和获取数据功能。 3.三种复制方法,原生JS,可以参考我写的方法,可兼容基本的IOS和安卓浏览器,适合简单场景。clipboard.js第三方库,兼容性较好,适合大型项目。Clipboard API 新的API,兼容性较好,可兼容同步异步数据,也推荐使用。 4.如果是PC端页面,推荐使用原生js去实现,代码量较少,引入简单。 ## 一点思考 当我们遇到要做复制功能时,首先应该考虑此功能和业务的相关性。 如果是一个很重要的功能,就像淘宝app内的复制口令码,在淘宝app内直接打开商品。银行app里的复制卡号,属于强交互功能,可以参考我下面的方案一二 如果只是一个不影响业务的部分,或者内部使用的系统,可以尝试新的API. # 附录 这是我做实验用的代码,大家也可直接复制,去自己真机验证 ```html 复制功能测试

三种复制粘贴方式验证

原生jsAPI document execCommand('copy')

第三方库函数 clipboard.js

最新API 剪贴板 Clipboard API

在此处粘贴

```

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值