Vue3自定义指令的注册和使用

254 阅读2分钟

局部注册

先在setup中进行局部注册,如果使用Composition API

<script setup>
import { nextTick, ref } from 'vue';

const overflowIndexes = ref(new Set());

const handleOverflow = (index) => (el) => {
  nextTick(() => {
    if (el.scrollWidth > el.clientWidth) {
      overflowIndexes.value.add(index);
    } else {
      overflowIndexes.value.delete(index);
    }
  });
};

// 自定义指令的名称和对象格式有一定的规范和要求(重点)
// v-overflow="handleOverflow(image.fileId)"调用时,binding.value为handleOverflow(image.fileId)函数
const vOverflow = {
  mounted(el, binding) {
    const callback = binding.value;
    callback(el);
  },
  updated(el, binding) {
    const callback = binding.value;
    callback(el);
  },
};
</script>

如果使用Options API

<script>
export default {
  directives: {
    overflow: {
      mounted(el, binding) {
        binding.value(el);
      },
      updated(el, binding) {
        binding.value(el);
      },
    },
  },
};
</script>

然后在template里使用

<el-tooltip :content="image.fileName" placement="top" :disabled="!overflowIndexes.has(image.fileId)">
  <div v-overflow="handleOverflow(image.fileId)" class="p-1 text-base leading-5 truncate">
    {{ image.fileName }}
  </div>
</el-tooltip>

全局注册

import { createApp } from 'vue';

const vOverflow = {
  mounted(el, binding) {
    binding.value(el);
  },
  updated(el, binding) {
    binding.value(el);
  },
};

const app = createApp(App);
app.directive('overflow', vOverflow); // 全局注册
app.mount('#app');

使用方式跟局部注册的一样。

重点之一:Vue自定义指令对象的格式

const myDirective = {
  // 1️⃣ 元素插入到 DOM 时调用 (只触发一次)
  mounted(el, binding, vnode, prevVnode) {
    console.log('mounted:', el, binding);
  },

  // 2️⃣ 组件更新时调用 (数据变化时触发)
  updated(el, binding, vnode, prevVnode) {
    console.log('updated:', el, binding);
  },

  // 3️⃣ 元素移除前调用
  beforeUnmount(el, binding, vnode, prevVnode) {
    console.log('beforeUnmount:', el, binding);
  },

  // 4️⃣ 元素移除后调用
  unmounted(el, binding, vnode, prevVnode) {
    console.log('unmounted:', el, binding);
  }
};

其中binding是一个对象,包含如下指令的值等信息
binding = {
  instance,  // 调用该指令的组件实例
  value,     // 绑定的值,如 v-myDirective="myValue",则 value = myValue
  oldValue,  // 之前的值(仅在 updated 中可用)
  arg,       // 传递的参数,如 v-myDirective:arg,arg = "arg"
  modifiers, // 修饰符,如 v-myDirective.modifier,modifiers = { modifier: true }
}

重点之二:Vue自定义指令名称的格式

推荐使用小驼峰(camelCase) Vue 自动将指令名称从 camelCase 转换为 短横线(kebab-case)。所以在js/ts中定义的vOverflow,可以在template中通过v-overflow调用。

具体转换规则:

- 小写字母保持不变
    * mydirective → mydirective
- 大写字母前加 - 并转换为小写
    * myDirective → my-directive
    * mySuperDirective → my-super-directive
- 多个大写字母连续时,每个字母前都加 -
    * myXMLParser → my-x-m-l-parser(一般避免这样写)
- 前导大写字母不受影响(Vue 习惯小写命名)
    * MyDirective → -my-directive(但 Vue 指令通常不会这样写)

另外, 在Vue 指令、组件名称和prop传递时,名称格式也会从camelCase 自动转换为 kebab-case。

应用camelCase(JS/TS 内部)kebab-case(Vue 模板中)
指令 (**vOverflow**)**vOverflow****v-overflow**
组件 (**MyComponent**)**MyComponent****<my-component />**
Props (**myPropValue**)**myPropValue****my-prop-value**
事件 (**myEvent**)**emit('myEvent')****@my-event="handleMyEvent"**
插槽 (**mySlot**)**#mySlot****#my-slot**