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

Is it possible to dispatch a svelte custom event with a target object?

是否可以像本机浏览器事件一样使用目标对象分派 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, target, detail) => {
    const callbacks = component.$$.callbacks[type];
    if (callbacks) {
      const event = new CustomEvent(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);
        });
      */
    }
  };
}

user5354671 注释掉 fn.call(...) 不会调用外部组件的回调。

target.dispatchEvent() 仅用于在该事件对象上添加目标

您可能希望明确传递 target

// Component.svelte
<script>
  import { createEventDispatcher } from 'svelte'
  const payload = 'foo'

  function onClick (event) {
    const { target } = event
    dispatch('myEvent', { target, payload })
  }
</script>

<button on:click={ onClick }></button>

然后,某处:

<script>
  import Component from 'Component.svelte'

  function handler (event) {
    const { target, payload } = event.detail
  }
</script>

<Component on:myEvent={ handler }/>