【发布时间】:2021-06-07 13:47:11
【问题描述】:
我有一个嵌套组件的层次结构 (Svelte 3.35.0),如下所示:
Component A
Component B (third-party)
Component C
组件 C 想要使用 createEventDispatcher 来 dispatch 一个事件,并且组件 A 正在侦听。文档说:
如果 on: 指令不带值使用,组件将转发事件,这意味着组件的使用者可以监听它。
问题是.. 默认行为将事件转发限制为“子 -> 父”,我无权访问组件 B 的代码(它是一个单独的库)。根据规则,我需要在定义的层次结构上“转发”该事件,即从 B 到 A。
因此,如果我想正确地将事件从组件 C“冒泡”到在组件 A 中定义的处理程序,我需要构建以下逻辑:
Component A have an implementation for the "onMyEventHandler" function and
it doesn't know anything about Component C (it shouldn't, for decoupling purposes).
Component A ---------------------- on:my-event={onMyEventHandler}
Component B ------------------ on:my-event <-- just to forward up to the parent's parent
Component C -------------- dispatch('my-event')
问题是 - 如何使用on:eventname 指令正确地将事件从组件 C 传递到组件 A?我在生命周期代码 (https://github.com/sveltejs/svelte/blob/v3.35.0/src/runtime/internal/lifecycle.ts#L64) 中看到了 bubble 方法,但我不明白如何正确利用该逻辑。还是有更方便的方法?
更新:还有像$$props和$$restProps这样的“神奇”道具,它们用于通过元素属性转发组件属性的值,但对于on:-like指令没有类似的“技巧” .
更新 2:因此,我应该提供一个示例(此外,此时我正在考虑使用 writable 存储和上下文)。
我正在使用svelte-routing 库,我的代码如下所示:
./Application.svelte
<Menu items={menuItems} />
...
<Router>
{#each menuItems as menuItem}
{#if menuItem.component !== MenuDivider}
<Route <--- Component B
path={menuItem.path}
component={menuItem.component} <--- Component C
on:app.event.page.shown={onPageShown}
/>
{/if}
{/each}
</Router>
./Navigation/Menu.svelte
<Router>
<ul class="menu">
{#each items as item}
{#if item.component === MenuDivider}
<MenuDivider label={item.title} />
{:else}
{#if item.url?.length > 0}
<li class="menu-item">
<Link to={item.url} getProps={onLinkUpdate}>
{item.title}
</Link>
</li>
{/if}
{/if}
{/each}
</ul>
</Router>
./Page/AboutPage.svelte
<div> page contents... </div>
Route 是来自外部库的“第三方”组件,它提供 API 来定义不同“虚拟”页面之间的客户端路由(即页面组件,请参见 component={menuItem.component} 行)。该页面有一个 onMount 和一些业务逻辑,用于通知主要应用程序组件不同的状态更改(例如标题附加)。因此它会发出事件,并且顶级应用程序组件会尝试捕获它们(并且它不知道我们有多少页面以及它们的名称)。
来自svelte-routing 的Route 具有以下签名:
{#if $activeRoute !== null && $activeRoute.route === route}
{#if component !== null}
<svelte:component this="{component}" location={$location} {...routeParams} {...routeProps} />
{:else}
<slot params="{routeParams}" location={$location}></slot>
{/if}
{/if}
如果我像这样向它放置一个转发on:onMyEventHandler 指令:
<svelte:component this="{component}" location={$location} {...routeParams} {...routeProps} on:onMyEventHandler />
它将使用以下路径传播事件:AboutPage (project-level) -> Route (library) -> Application (project-level)。但是,很明显,我无法做到这一点(并且没有 "$$restDirectives"/"$$forwardMePlease" 技巧可以做到这一点)。
我认为这只是一种糟糕的做事方式,认为“事件”概念是一个全局性的东西(来自 java/php 样式的事件调度程序)。
【问题讨论】:
-
很好奇,但是,当我开始我的项目时(
3.24.0) - 这是可能的(不知道如何)。升级到3.35.0后 - 我的带有事件调度逻辑的代码不再工作了:) -
如果 B 是第三方库,如何让 C 成为 B 的孩子?如果这是通过使用插槽,您可以直接在插槽 B 上添加事件。
-
增加了一个例子。是的,我可以(至少
3.24.0版本是这样),但 B 只是路由库和“页面组件”(C)之间的“桥梁”,它为主应用程序组件(A)发出事件,例如更改共享页面标题。 -
您特别询问了如何使用 Svelte 的
on:样式事件来做到这一点。如果这不是一成不变的,您可以使用自定义 dom 事件,例如我的示例:svelte.dev/repl/f1b23c6b95be4dfb9cad18dbba20d1f1?version=3.35.0 -
哇!这完全符合我的要求,谢谢您的分享,先生!因此,实际的解决方案可能是 - 在 DOM 上使用某种“全局”事件或其他杠杆,如上下文/存储。
标签: svelte