是否可以使用目标对象分派苗条的自定义事件?

问题描述

是否可以使用像原生浏览器事件这样的目标对象来分派一个 svelte 事件(使用 createEventdispatcher 创建)?

即在处理程序端接收 event.target.value 而不是 event.detail。

解决方法

是的,这是可能的,但涉及一些黑客攻击。

您可以查看 svelte 源代码以了解事件调度的工作原理。我将概述以下关键部分。

假设我们有两个组件,InnerOuter

<!-- Inner.svelte -->
<script>
import { createEventDispatcher } from 'svelte'
const dispatch = createEventDispatcher()
const dispatchFoo = () => dispatch('foo','bar')
</script>

<button on:click={dispatchFoo}>send</button>

<!-- Outer.svelte -->
<script>
import Inner from './Inner.svelte'
const handleFoo = (e) => { console.log('receive foo',e) }
</script>

<Inner on:foo={handleFoo}></Inner>

仔细想想,事件处理程序 handleFoo 是在 Outer 中创建的,但它实际上是在 Inner 上注册的。

检查你将看到的编译后的 JS 代码:

  1. inner.$$.callbacks["foo"] 持有事件处理程序 [src]
  2. 当您单击按钮并调用 dispatch 时,它只会读取潜在处理程序的 inner.$$.callbacks["foo"],如果找到,则使用 CustomEvent 作为参数调用它们 [src]

设置 customEvent.target 的唯一方法是使用 element.dispatchEvent(customEvent) 调度该自定义事件。但是 element.dispatchEvent 并没有贯穿整个过程。

解决方案(黑客)

编写您自己的createEventDispatcher

import { get_current_component } from 'svelte/internal'

function createEventDispatcher() {
  const component = get_current_component();
  return (type,target,detail) => {
    const callbacks = component.$$.callbacks[type];
    if (callbacks) {
      const event = new CustomEvent(type,{ detail });
      // the key is to call `dispatchEvent` manually to set `event.target`
      target.dispatchEvent(event);
      callbacks.slice().forEach((fn) => {
        fn.call(component,event);
      });
    }
  };
}

Svelte REPL

,

根据 hackape 的回答修改

import { get_current_component } from 'svelte/internal'

function createEventDispatcher() {
  const component = get_current_component(bubbles = true);
  return (type,{ bubbles,detail });
      // the key is to call `dispatchEvent` manually to set `event.target`
      target.dispatchEvent(event);
      /* You have already raised an event,you should not repeat the callbacks to avoid duplication
        callbacks.slice().forEach((fn) => {
          fn.call(component,event);
        });
      */
    }
  };
}