关于Vue3自定义指令三两事~

219 阅读4分钟

近来摸鱼吃东西时想要复制个内容,下意识的用了左手去cv十分不便,突然想到Vue中提供的自定义指令,作为Vue中最常使用的内容之一,方便的指令是开发过程中不可或缺的方法,于是便将自己2.x项目中的自定义指定进行了下升级。顺道重新复习下相关知识,如果这篇文章对你有帮助自是不胜荣幸。

1.什么是自定义指令?

所谓自定义指令其实就是指令当中的一个分支用法,那么什么是指令呢? 我们平常开发中使用的 v-ifv-showv-forv-model... 等都是Vue内置的核心指令,既内置指令。通过这些内置指令我们可以对dom进行底层操作。而自定义就是Vue为了满足我们开发者的个性化需求而提供的API,通过它可以更方便的完成需求开发。

2.使用自定义指令

那么如何使用自定义指令呢?想像内置指令一样使用自定义指令自然是离不开注册再使用。同样的注册指令也分为全局注册和局部注册,全局注册的指令可以在任何组件中直接使用,局部注册的指令只能在注册的地方使用。

2.1 钩子函数

不管是Vue的内置指令还是自定义指令,都有类似于组件的生命周期,我们可以在不同的生命周期完成不同的逻辑操作,并绑定到组件元素上,这样就完成了自定义指令的生成。

一个自定义指令对象可以提供如下几个钩子函数 (均为可选):

  • created:在绑定元素的 attribute 或事件监听器被应用之前调用。在指令需要附加在普通的 v-on 事件监听器调用前的事件监听器中时,这很有用。
  • beforeMount:当指令第一次绑定到元素并且在挂载父组件之前调用。
  • mounted:在绑定元素的父组件被挂载后调用。
  • beforeUpdate:在更新包含组件的 VNode 之前调用。
  • updated:在包含组件的 VNode 及其子组件的 VNode 更新后调用。
  • beforeUnmount:在卸载绑定元素的父组件之前调用
  • unmounted:当指令与元素解除绑定且父组件已卸载时,只调用一次。

钩子函数的参数 (即 elbindingvnode 和 prevVnode

具体参数信息可移步至Vue3官方文档

2.2 全局注册

Vue提供了一个 directive 方法给我们注册自定义指令,和组件的全局注册一样,我们在main.js中注册了一个全局自定义指令后,自定义指令可在项目中所有组件内使用。

/*main.js*/ 
import { createApp } from 'vue'
import App from './App.vue'
import copy from "../src/util/copy";

const app = createApp(App);
// 注册一个全局自定义指令 `v-focus`
app.directive('focus', {
  // 当被绑定的元素挂载到 DOM 中时……
  mounted(el) {
    // 聚焦元素
    el.focus()
  }
})

注册成功后便可在组件中使用了

<input type="text" v-focus />

2.3 局部注册

Vue提供了配置项 directives ,可以让我们在组件中通过 directives 选项进行局部注册。

/*组件内部局部注册*/
<template>
  <input type="text" v-focus />
</template>

<script>
  export default {
     setup() {},
    directives: {
      focus: {
        mounted(el, bindings, vnode, preVnode) {
          console.log("节点已挂载完成");
          el.focus();
        }
      }
    }
  }
</script>

这样一来通过自定义指令 v-focus 就可以实现,当某个元素挂载完成后可以自定获取焦点这个功能。 当然自定义指令自然不止这点用处,在我们的开发中有许多比较常见的需求,便可以通过自定义指令完成,比如一键复制粘贴、输入框防抖、图片懒加载等等便不一一列举,下面通过全局注册来自定义一个常用指令吧。

3.常用自定义指令示例

以一键复制为例,首先先确定需求与思路逻辑。

需求:实现一键复制当前文本,可通过Ctrl+V或者鼠标右键完成粘贴。

实现思路:

  1. 创建 textarea 标签,设置 readOnly 属性移出可视区
  2. 将要复制的值赋给 textarea 标签的 value 属性,并插入到 body
  3. 选中textarea的值并复制
  4. 将 body 中插入的 textarea 移除
  5. 在第一次调用时绑定事件,在解绑时移除事件

全局注册自定义指令

/* main.js */
import {
    createApp
} from 'vue'
import App from './App.vue'
import router from "./router";
import ElementPlus from 'element-plus';
import 'element-plus/dist/index.css';
import copy from "../src/util/copy"; // 引入copy方法文件

const app = createApp(App);
app.use(router)
app.use(ElementPlus, {
    size: 'small',
    zIndex: 3000
})
copy(app) // 传入app实例

app.mount('#app')

一键复制逻辑代码:

/* util/copy.js */
import { ElMessage } from "element-plus";
export default app => {
  const copy = {
    mounted(el, { value }) {
      el.$value = value;
      el.handler = () => {
        console.log("要复制的内容是:", value);
        if (!el.$value) {
          return ElMessage({
            message: "空的!空的!空的!",
            type: "warning"
          });
        }
        // 动态创建 textarea 标签
        const textarea = document.createElement("textarea");
        // 将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();
        const result = document.execCommand("Copy");
        if (result) {
          ElMessage({
            message: "复制成功!",
            type: "success"
          });
        }
        // 移除textarea
        document.body.removeChild(textarea);
      };
      el.addEventListener("click", el.handler);
    },
    // 传值更新的时触发
    updated(el, { value }) {
      el.$value = value;
    },
    // 指令与元素解绑的时候,移除事件绑定
    unbind(el) {
      el.removeEventListener("click", el.handler);
    }
  };
  app.directive("copy", copy); // 为实例注册方法
};

组件中使用自定义指令:

<template>
  <div>
    问:<el-input v-model="text1"></el-input>
    <el-button v-copy="text1">复制</el-button>
  </div>
  <div>
    答: <el-input v-model="text2"></el-input>
    <el-button @click="pasteEvent(text1)">赋值</el-button>
  </div>
</template>

<script>
import { ElMessage } from "element-plus";
import { ref } from "@vue/runtime-core";
export default {
  setup() {
    const text1 = ref("隐约雷鸣,阴霾天空。但盼风风雨来,能留你在此");
    let text2 = ref("");

    const pasteEvent = value => {
      const text = value.split("。");
      text2.value = `${text[0]},即使天无雨,我亦留此地`;
      ElMessage({
        message: "粘贴成功!",
        type: "success"
      });
    };

    return {
      text1,
      text2,
      pasteEvent
    };
  }
};
</script>

实现效果:

一键复制.gif

以上就是Vue3中自定义指令的用法,如有问题欢迎指正~