天天看點

撸一個 Vue 自定義指令實作一鍵 Copy的功能

話不多說先看效果,這個效果是用在真實項目中的實際效果哈:

撸一個 Vue 自定義指令實作一鍵 Copy的功能

指令是啥?

按照慣例,先請出官方的解釋:

指令 (Directives) 是帶有 v- 字首的特殊特性。指令特性的值預期是單個 JavaScript 表達式 (v-for 是例外情況,稍後我們再讨論)。指令的職責是,當表達式的值改變時,将其産生的連帶影響,響應式地作用于 DOM。

再按照慣例,大家 ( 假裝 ) 看不懂,然後我來舉個栗子解釋一番。。。好,還是不知所雲,本文結束 (

Ctrl + F4

) ,下一篇。

為了避免上述情況出現,就不解釋了。實際上官方提供了很多内置指令,如:

v-if

v-for

v-bind

and so on。每一個指令都有自己特定的功能。

自定義指令

顧名思義就是自己定義的指令啦,可以實作我們想要的功能。下面就實作一個 一鍵

Copy

的功能吧。

生命周期

首先簡單瞟一下指令的文法,每個指令都有自己的生命周期,看到生命周期,肯定會想到鈎子函數,沒錯,指令也提供了鈎子函數:

  • bind

    :指令第一次綁定到元素時調用,此鈎子隻會調用一次。在這裡可以進行一次性的初始化設定。
  • inserted

    :被綁定元素插入父節點時調用 (僅保證父節點存在,但不一定已被插入文檔中)。
  • update

    :所在元件的 VNode 更新時調用,但是可能發生在其子 VNode 更新之前。指令的值可能發生了改變,也可能沒有。
  • componentUpdated

    :指令所在元件的 VNode 及其子 VNode 全部更新後調用。
  • unbind

    :隻調用一次,指令與元素解綁時調用。

下面再簡單瞟一眼上述鈎子函數的參數哈:

  • el

    :指令所綁定的元素,可以用來直接操作

    DOM

  • binding

    :一個對象,包含以下屬性:
  • name

    :指令名,不包括

    v-

    字首。
  • value

    :指令的綁定值,例如:

    v-my-directive="1 + 1"

    中,綁定值為

    2

  • oldValue

    :指令綁定的前一個值,僅在

    update

    componentUpdated

    鈎子中可用。無論值是否改變都可用。
  • expression

    :字元串形式的指令表達式。例如

    v-my-directive="1 + 1"

    中,表達式為 “

    1 + 1

    ”。
  • arg

    :傳給指令的參數,可選。例如

    v-my-directive:foo

    中,參數為 “

    foo

    ”。
  • modifiers

    :一個包含修飾符的對象。例如:

    v-my-directive.foo.bar

    中,修飾符對象為

    { foo: true, bar: true }

  • vnode

    Vue

    編譯生成的虛拟節點。移步

    VNode API

    來了解更多詳情。
  • oldVnode

    :上一個虛拟節點,僅在

    update

    componentUpdated

    鈎子中可用。

看起來還挺多的,不過别方,其實常用的就幾個。好了下面要開始表演了:

等等,還是再捋一下思路吧:

  • 怎麼

    copy

    :實際上浏覽器提供了原生 api,但是隻能

    copy

    選中的值(就是滑鼠選中的那種)。
  • 那怎麼用代碼實作選中的效果呢?這就要用到輸入控件(input、textarea…)了,該控件提供了選中的

    api

    ,選中的就是目前控件的

    value

    值。
  • 那思路就有了:
    • 動态建立

      input

      标簽
    • 把要複制的值賦給

      input.value

    • 調用

      api

      選中

      input.value

    • 最後調用

      api

      複制選中的值
  1. 首先建一個

    js

    檔案(v-copy.js)。定義一個對象。( 指令實際就是一個對象 )
    import { Message } from 'ant-design-vue';
    
    const vCopy = { // 名字愛取啥取啥
      /*
        bind 鈎子函數,第一次綁定時調用,可以在這裡做初始化設定
        el: 作用的 dom 對象
        value: 傳給指令的值,也就是我們要 copy 的值
      */
      bind(el, { value }) {
        el.$value = value; // 用一個全局屬性來存傳進來的值,因為這個值在别的鈎子函數裡還會用到
        el.handler = () => {
          if (!el.$value) {
          // 值為空的時候,給出提示,我這裡的提示是用的 ant-design-vue 的提示,你們随意
            Message.warning('無複制内容');
            return;
          }
          // 動态建立 textarea 标簽
          const textarea = document.createElement('textarea');
          // 将該 textarea 設為 readonly 防止 iOS 下自動喚起鍵盤,同時将 textarea 移出可視區域
          textarea.readOnly = 'readonly';
          textarea.style.position = 'absolute';
          textarea.style.left = '-9999px';
          // 将要 copy 的值賦給 textarea 标簽的 value 屬性
          textarea.value = el.$value;
          // 将 textarea 插入到 body 中
          document.body.appendChild(textarea);
          // 選中值并複制
          textarea.select();
          textarea.setSelectionRange(0, textarea.value.length);
          const result = document.execCommand('Copy');
          if (result) {
            Message.success('複制成功');
          }
          document.body.removeChild(textarea);
        };
        // 綁定點選事件,就是所謂的一鍵 copy 啦
        el.addEventListener('click', el.handler);
      },
      // 當傳進來的值更新的時候觸發
      componentUpdated(el, { value }) {
        el.$value = value;
      },
      // 指令與元素解綁的時候,移除事件綁定
      unbind(el) {
        el.removeEventListener('click', el.handler);
      },
    };
    
    export default vCopy;
               
  2. 到這裡,一鍵

    Copy

    的功能就實作了,最後再說一嘴怎麼将自定義指令注冊到全局:再建立一個

    js

    ( directives.js )檔案來注冊所有的全局指令。
    import copy from './v-copy';
    // 自定義指令
    const directives = {
      copy,
    };
    // 這種寫法可以批量注冊指令
    export default {
      install(Vue) {
        Object.keys(directives).forEach((key) => {
          Vue.directive(key, directives[key]);
        });
      },
    };
               
  3. 最後,在

    main.js

    中這樣引入:
    import Vue from 'vue';
    import Directives from './directives';
    
    Vue.use(Directives);
               
  4. 最後的最後,說一下怎麼用吧。。
    <template>
      <button v-copy="copyText">copy</button>
    </template>
    
    <script>
    export default {
      data() {
        return {
          copyText: '要 Copy 的内容',
        };
      },
    };
    </script>
               

OK 全部搞完了,撒花。