vue-clipboard2在vue的created生命周期中直接调用copyText方法报错的原因分析

本文主要是介绍vue-clipboard2在vue的created生命周期中直接调用copyText方法报错的原因分析,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

vue-clipboard2在vue的created生命周期中直接调用copyText方法报错

先说现象:在created生命周期中会进入reject状态(被catch到),不在生命周期的方法中调用而通过click事件来调用会正常进入resolved状态(成功进入then阶段)。

下面进行相关源码分析:

出错代码:

created() {this.$copyText('asdasdasdas').then(() => {console.log('复制成功');}).catch(err => {console.error('复制出错', err); // 执行到这里了})
},

打开vue-clipboard2的源码,可以发现底层使用了clipboard这个库:

Vue.prototype.$copyText = function (text, container) {return new Promise(function (resolve, reject) {var fakeElement = document.createElement('button')// 注意这里,使用了Clipboard的构造方法var clipboard = new Clipboard(fakeElement, {text: function () { return text },action: function () { return 'copy' },container: typeof container === 'object' ? container : document.body})clipboard.on('success', function (e) {clipboard.destroy()resolve(e)})// 注意这里clipboard.on('error', function (e) {clipboard.destroy()reject(e)})if (VueClipboardConfig.appendToBody) document.body.appendChild(fakeElement)fakeElement.click()if (VueClipboardConfig.appendToBody) document.body.removeChild(fakeElement)})
}

在它的package.json中找到clipboard的依赖,确保别找错了:

"dependencies": {"clipboard": "^2.0.0"
},

再看一下我们安装的clipboard的版本:

"version": "2.0.4"

github上搜一下这个库的源码:https://github.com/zenorocha/clipboard.js

方便查找代码的引用关系,我们去这个网址:https://sourcegraph.com/github.com/zenorocha/clipboard.js@master/-/blob/src/clipboard.js

之前的代码调用了clipboard的构造方法:

 constructor(trigger, options) {super();this.resolveOptions(options);this.listenClick(trigger);}/*** Defines if attributes would be resolved using internal setter functions* or custom functions that were passed in the constructor.* @param {Object} options*/resolveOptions(options = {}) {this.action    = (typeof options.action    === 'function') ? options.action    : this.defaultAction;this.target    = (typeof options.target    === 'function') ? options.target    : this.defaultTarget;this.text      = (typeof options.text      === 'function') ? options.text      : this.defaultText;this.container = (typeof options.container === 'object')   ? options.container : document.body;}/*** Adds a click event listener to the passed trigger.* @param {String|HTMLElement|HTMLCollection|NodeList} trigger*/listenClick(trigger) {this.listener = listen(trigger, 'click', (e) => this.onClick(e));}

triggervue-clipboard2传进来的button实例,listenClick做的就是给这个button加上click事件,再看一下onClick方法:

    onClick(e) {// button实例 delegateTarget是事件委托dom,这里我们走的是currentTargetconst trigger = e.delegateTarget || e.currentTarget;if (this.clipboardAction) {this.clipboardAction = null;}this.clipboardAction = new ClipboardAction({action    : this.action(trigger), // 'copy'target    : this.target(trigger), // undefinedtext      : this.text(trigger), // 'text' => 传入的text参数container : this.container, // 默认为bodytrigger   : trigger,emitter   : this});}

this.target方法再初始化时被定义成下面这个函数,因为vue-clipboard2没有传这个参数

    defaultTarget(trigger) {const selector = getAttributeValue('target', trigger); // 返回undefined,因为button没有target这个属性 if (selector) {return document.querySelector(selector);}}

下面再看看ClipboardAction的构造方法做了什么:

 constructor(options) {this.resolveOptions(options);this.initSelection();}/*** Defines base properties passed from constructor.* @param {Object} options*/resolveOptions(options = {}) {this.action    = options.action;this.container = options.container;this.emitter   = options.emitter;this.target    = options.target;this.text      = options.text;this.trigger   = options.trigger;this.selectedText = '';}/*** Decides which selection strategy is going to be applied based* on the existence of `text` and `target` properties.*/initSelection() {if (this.text) {this.selectFake();}else if (this.target) {this.selectTarget();}}

主要是对传进来的参数进行本地赋值,看到initSelection方法,进入了第一个分支:

    selectFake() {const isRTL = document.documentElement.getAttribute('dir') == 'rtl';// 这个方法做的事情是删除textarea节点,清除container上的click事件// 将fakeHandler,fakeHandlerCallback,fakeElem置为nullthis.removeFake();this.fakeHandlerCallback = () => this.removeFake();this.fakeHandler = this.container.addEventListener('click', this.fakeHandlerCallback) || true;this.fakeElem = document.createElement('textarea');// Prevent zooming on iOSthis.fakeElem.style.fontSize = '12pt';// Reset box modelthis.fakeElem.style.border = '0';this.fakeElem.style.padding = '0';this.fakeElem.style.margin = '0';// Move element out of screen horizontallysthis.fakeElem.style.position = 'absolute';this.fakeElem.style[ isRTL ? 'right' : 'left' ] = '-9999px';// Move element to the same position verticallylet yPosition = window.pageYOffset || document.documentElement.scrollTop;this.fakeElem.style.top = `${yPosition}px`;this.fakeElem.setAttribute('readonly', '');this.fakeElem.value = this.text;this.container.appendChild(this.fakeElem);this.selectedText = select(this.fakeElem);this.copyText();}

创建了一个textareadom节点,value为我们传进去的text

select方法为外部依赖,做的事情是帮我们选中textarea中的文字。接下来调用了copyText方法,

    /*** Executes the copy operation based on the current selection.*/copyText() {let succeeded;try {succeeded = document.execCommand(this.action); // this.action === 'copy'}catch (err) {succeeded = false;}this.handleResult(succeeded);}

可以看到调用了execCommand方法来执行操作系统的copy方法,而我们报的错是在handleResultemit出来的,所以我们的$copyText方法进入了catch分支。

    // vue-clipboard2的监听事件clipboard.on('error', function (e) {clipboard.destroy()reject(e)})  handleResult(succeeded) {this.emitter.emit(succeeded ? 'success' : 'error', {action: this.action,text: this.selectedText,trigger: this.trigger,clearSelection: this.clearSelection.bind(this)});}

也就是说succeeded变量值为false,这一点在我们断点调试一下可以发现确实返回了fasle。

在这里插入图片描述

为什么呢?先看一下MDN文档对于execCommand方法的说明:

bool = document.execCommand(aCommandName, aShowDefaultUI, aValueArgument);
// 一个 Boolean ,如果是 false 则表示操作不被支持或未被启用。
// 注意:在调用一个命令前,不要尝试使用返回值去校验浏览器的兼容性

可是我的浏览器是chrome 78,按理说支持这个方法啊,可是为什么会返回false呢?

返回false的原因其实也是浏览器对安全性的考虑,因为copy这个操作不是由用户操作产生的,而是由代码自执行的,所以默认执行失败。

document.execCommand的特殊性

浏览器处于安全考虑,document.execCommand这个api只能在真正的用户操作之后才能被触发。

以下引用自W3C草案:

If an implementation supports ways to execute clipboard commands through scripting, for example by calling the document.execCommand() method with the commands “cut”, “copy” and “paste”, the implementation must trigger the corresponding action, which again will dispatch the associated clipboard event.

copy事件的执行过程:

  1. If the script-triggered flag is set, then
    1. If the script-may-access-clipboard flag is unset, then
      1. Return false from the copy action, terminate this algorithm
  2. Fire a clipboard event named copy
  3. If the event was not canceled, then
    1. Copy the selected contents, if any, to the clipboard. Implementations should create alternate text/html and text/plain clipboard formats when content in a web page is selected.
    2. Fire a clipboard event named clipboardchange
  4. Else, if the event was canceled, then
    1. Call the write content to the clipboard algorithm, passing on the DataTransferItemList list items, a clear-was-called flag and a types-to-clear list.
  5. Return true from the copy action

参考链接

Cannot use document.execCommand('copy'); from developer console

execCommand(‘copy’) does not work in Ajax / XHR callback?

W3C:Clipboard API and events

这篇关于vue-clipboard2在vue的created生命周期中直接调用copyText方法报错的原因分析的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/246924

相关文章

MySQL中的LENGTH()函数用法详解与实例分析

《MySQL中的LENGTH()函数用法详解与实例分析》MySQLLENGTH()函数用于计算字符串的字节长度,区别于CHAR_LENGTH()的字符长度,适用于多字节字符集(如UTF-8)的数据验证... 目录1. LENGTH()函数的基本语法2. LENGTH()函数的返回值2.1 示例1:计算字符串

Java中读取YAML文件配置信息常见问题及解决方法

《Java中读取YAML文件配置信息常见问题及解决方法》:本文主要介绍Java中读取YAML文件配置信息常见问题及解决方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要... 目录1 使用Spring Boot的@ConfigurationProperties2. 使用@Valu

Android kotlin中 Channel 和 Flow 的区别和选择使用场景分析

《Androidkotlin中Channel和Flow的区别和选择使用场景分析》Kotlin协程中,Flow是冷数据流,按需触发,适合响应式数据处理;Channel是热数据流,持续发送,支持... 目录一、基本概念界定FlowChannel二、核心特性对比数据生产触发条件生产与消费的关系背压处理机制生命周期

Java 方法重载Overload常见误区及注意事项

《Java方法重载Overload常见误区及注意事项》Java方法重载允许同一类中同名方法通过参数类型、数量、顺序差异实现功能扩展,提升代码灵活性,核心条件为参数列表不同,不涉及返回类型、访问修饰符... 目录Java 方法重载(Overload)详解一、方法重载的核心条件二、构成方法重载的具体情况三、不构

SQL中如何添加数据(常见方法及示例)

《SQL中如何添加数据(常见方法及示例)》SQL全称为StructuredQueryLanguage,是一种用于管理关系数据库的标准编程语言,下面给大家介绍SQL中如何添加数据,感兴趣的朋友一起看看吧... 目录在mysql中,有多种方法可以添加数据。以下是一些常见的方法及其示例。1. 使用INSERT I

Python中反转字符串的常见方法小结

《Python中反转字符串的常见方法小结》在Python中,字符串对象没有内置的反转方法,然而,在实际开发中,我们经常会遇到需要反转字符串的场景,比如处理回文字符串、文本加密等,因此,掌握如何在Pyt... 目录python中反转字符串的方法技术背景实现步骤1. 使用切片2. 使用 reversed() 函

Python中将嵌套列表扁平化的多种实现方法

《Python中将嵌套列表扁平化的多种实现方法》在Python编程中,我们常常会遇到需要将嵌套列表(即列表中包含列表)转换为一个一维的扁平列表的需求,本文将给大家介绍了多种实现这一目标的方法,需要的朋... 目录python中将嵌套列表扁平化的方法技术背景实现步骤1. 使用嵌套列表推导式2. 使用itert

Python使用pip工具实现包自动更新的多种方法

《Python使用pip工具实现包自动更新的多种方法》本文深入探讨了使用Python的pip工具实现包自动更新的各种方法和技术,我们将从基础概念开始,逐步介绍手动更新方法、自动化脚本编写、结合CI/C... 目录1. 背景介绍1.1 目的和范围1.2 预期读者1.3 文档结构概述1.4 术语表1.4.1 核

在Linux中改变echo输出颜色的实现方法

《在Linux中改变echo输出颜色的实现方法》在Linux系统的命令行环境下,为了使输出信息更加清晰、突出,便于用户快速识别和区分不同类型的信息,常常需要改变echo命令的输出颜色,所以本文给大家介... 目python录在linux中改变echo输出颜色的方法技术背景实现步骤使用ANSI转义码使用tpu

Conda与Python venv虚拟环境的区别与使用方法详解

《Conda与Pythonvenv虚拟环境的区别与使用方法详解》随着Python社区的成长,虚拟环境的概念和技术也在不断发展,:本文主要介绍Conda与Pythonvenv虚拟环境的区别与使用... 目录前言一、Conda 与 python venv 的核心区别1. Conda 的特点2. Python v