是否可以使用目标对象分派 svelte 自定义事件?
Is it possible to dispatch a svelte custom event with a target object?
是否可以像本机浏览器事件一样使用目标对象分派 svelte 事件(使用 createEventDispatcher
创建)?
即在处理程序端接收 event.target.value
而不是 event.detail.
是的,这是可能的,但涉及一些黑客攻击。
您可以查看 svelte 源代码以了解事件调度的工作原理。我将重点部分概括如下。
假设我们有两个组件,Inner
和 Outer
。
<!-- 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 代码,您将看到:
inner.$$.callbacks["foo"]
持有事件处理程序 [src]
- 当您单击按钮并调用
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);
});
}
};
}
根据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 }/>
是否可以像本机浏览器事件一样使用目标对象分派 svelte 事件(使用 createEventDispatcher
创建)?
即在处理程序端接收 event.target.value
而不是 event.detail.
是的,这是可能的,但涉及一些黑客攻击。
您可以查看 svelte 源代码以了解事件调度的工作原理。我将重点部分概括如下。
假设我们有两个组件,Inner
和 Outer
。
<!-- 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 代码,您将看到:
inner.$$.callbacks["foo"]
持有事件处理程序 [src]- 当您单击按钮并调用
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);
});
}
};
}
根据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 }/>