vue 3 指令监听 vue 发射

vue 3 directives listen vue emit

我想实现的是构建一个加载vue指令,
带有 v-loading 指令的函数将呈现微调器并阻止事件,直到函数承诺解析或拒绝。

到目前为止我尝试了什么:

  1. 使用addEventListener,但是只能监听dom的原生事件,不能监听vue事件
  2. 劫持 vue $emit 函数,但收到警告说不要覆盖名为 $ 的 vue 原生函数,即使这个解决方案有效,我认为这是一个糟糕的解决方案。
  3. 在指令参数中,binding.instance[binding.value.name] 引用组件中的 onEvent 函数,我尝试覆盖它但它不起作用。当 onEvent 再次触发时,它 运行 覆盖之前的旧 onEvent。
  4. 第三方事件发射器(例如手套)。此方法效果很好,但自定义组件必须编写额外的代码才能发出事件。
    作为下面的示例代码,
    v-loading 的用户必须记住写 2 emit(mitt 和 vue 的 emit)。
    它不是那么简单,而且有额外的依赖性。
// mitt solution
// custom-component template
<custom-component v-loading:event="onEvent">
// custom-component script
setup(props, {emit}) {
  function emitEvent() {
    emit("event");
    // bad: have to remember to write this extra line, and it is third party dependency
    mittEmitter.emit("event");
  }
}

那么,还有其他解决方案可以从 vue 的 $emit 监听 vue 的事件(不是 dom 的本地事件)吗?


LoadingDirective.ts

import { Directive } from "vue";

const Loading: Directive = {
  mounted(el, binding) {
    const eventName = binding.arg;
    const onEvent = binding.value;
    // I want to listen vue's event(eventName) here
    // do something extra
    onEvent();  // original onEvent() function to run in App.vue
    // do something extra
  }
};
export default Loading;

App.vue

<template>
  <!-- when onEvent triggered, a spinner will be render in custom-component -->
  <custom-component v-loading:event="onEvent" />
</template>

CustomComponent.vue

<script lang="ts">
import { defineComponent } from "vue";

export default defineComponent({
  setup(props, {emit}) {
    function emitEvent() {
      // use only vue's emit
      emit("event")
    }
    return {
      onEvent
    };
  }
});
</script>

Vue 3 文档 recommends 使用外部库,例如 mitttiny-emitter

JSFiddle Example

<template>
    <div id="app">
        <custom-component v-loading="eventHandler" />
    </div>
</template>
const emitter = mitt();

const customComponent = { template: '<h1>Example</h1>' };

const app = Vue.createApp({
    components: { customComponent },

    setup() {
        setTimeout(() => {
            emitter.emit('loadingEvent', { colour: 'Green' });
        }, 1000);
        
        const eventHandler = e => console.log('Handled!', e);
        
        return { eventHandler };
    },
});

app.directive('loading', {
    mounted(el, binding) {
        const func = binding.value;

        emitter.on('loadingEvent', data => {
            // your logic here...
            func(data);
        });
    },
});

app.mount('#app');