事件转发与回调 Props
本教程共 50 篇 · 第 19 篇 · 更新于 2026-08-05 · 约 6 分钟阅读
19. 事件转发与回调 Props
本节目标:学会 Svelte 5 中组件通信的新方式——回调 Props 模式,理解事件转发机制,了解 $host() rune 在自定义元素中的用途。
Svelte 5 的组件通信变了
在 Svelte 4 里,组件要往外发事件,你得用 createEventDispatcher 创建一个派发器,然后调用 dispatch('事件名') 来发事件。这套机制在 Svelte 5 中被废弃了。
Svelte 5 的思路很简单:事件就是函数,函数就是 Prop。把回调函数当作 Prop 传进去,子组件直接调用就行。
打个比方,以前你要给同事传话,得通过前台转接(createEventDispatcher)。现在直接把同事电话给你(回调 Prop),你自己打过去就行,简单直接。
回调 Props 模式
回调 Props 的核心思想:父组件把一个函数传给子组件,子组件在合适的时机调用这个函数。
<!-- Stepper.svelte -->
<script>
let { onincrement, ondecrement } = $props();
</script>
<button onclick={ondecrement}>减一</button>
<button onclick={onincrement}>加一</button>
<!-- App.svelte -->
<script>
import Stepper from './Stepper.svelte';
let count = $state(0);
</script>
<Stepper
onincrement={() => count += 1}
ondecrement={() => count -= 1}
/>
<p>当前计数:{count}</p>
你看到了吗?子组件通过 $props() 接收 onincrement 和 ondecrement 两个函数,然后在按钮点击时调用它们。父组件传入具体的逻辑。
Note回调 Props 的命名习惯是以
on开头,比如onclick、onsubmit、onchange。这样跟原生事件属性风格一致,一看就知道是事件回调。
回调 Props 传参
回调函数可以携带数据。子组件调用时传入参数,父组件就能收到。
<!-- SearchBox.svelte -->
<script>
let keyword = $state('');
let { onsearch } = $props();
function handleKeydown(event) {
if (event.key === 'Enter') {
// 把搜索词传给父组件
onsearch?.(keyword);
}
}
</script>
<input
bind:value={keyword}
onkeydown={handleKeydown}
placeholder="输入关键词后回车"
/>
<!-- App.svelte -->
<script>
import SearchBox from './SearchBox.svelte';
let results = $state([]);
function handleSearch(keyword) {
// 收到子组件传来的关键词
console.log('搜索:', keyword);
results = ['结果1', '结果2'];
}
</script>
<SearchBox onsearch={handleSearch} />
<ul>
{#each results as item}
<li>{item}</li>
{/each}
</ul>
Tip用可选链
onsearch?.()调用回调是个好习惯。这样即使父组件没传这个 Prop,也不会报错。
事件转发
有时候你的组件是个”中间人”——它包裹了一个子组件,需要把子组件的事件转发给更上层的父组件。
在 Svelte 5 里,事件转发可以用展开语法实现。把接收到的 Props 原样展开到子组件上就行。
<!-- Button.svelte -->
<script>
let { onclick, children, ...rest } = $props();
</script>
<button {onclick} {...rest}>
{@render children?.()}
</button>
<!-- FancyButton.svelte(中间组件) -->
<script>
import Button from './Button.svelte';
// 把所有 props 原样传给 Button
let props = $props();
</script>
<Button {...props} class="fancy" />
<!-- App.svelte -->
<script>
import FancyButton from './FancyButton.svelte';
function handleClick() {
console.log('点击事件穿过了两层组件');
}
</script>
<!-- onclick 会从 App 传到 FancyButton,再传到 Button -->
<FancyButton onclick={handleClick}>点我</FancyButton>
Note因为 Svelte 5 的事件就是普通属性,所以事件转发本质上就是属性转发。用
{...props}展开语法就能把所有事件回调一并传下去,非常方便。
对比 Svelte 4 的事件转发
来看看 Svelte 4 是怎么做事件转发的,你就知道 Svelte 5 有多简洁了。
<!-- Svelte 4 写法:Button.svelte -->
<button on:click>转发 click 事件</button>
<!-- Svelte 4 写法:FancyButton.svelte -->
<script>
import Button from './Button.svelte';
</script>
<!-- 需要手动转发每个事件:on:click 不带值表示转发 -->
<Button on:click />
Svelte 4 里你得为每个事件都写一条转发指令。Svelte 5 用展开语法一行搞定,所有 on 开头的属性自动转发。
$host() Rune
$host() 是一个特殊的 rune,专门用于把 Svelte 组件编译成自定义元素(Web Components)时使用。它返回宿主元素,让你能在自定义元素上派发事件。
<!-- Stepper.svelte -->
<svelte:options customElement="my-stepper" />
<script>
function dispatch(type) {
// $host() 返回自定义元素的 DOM 节点
$host().dispatchEvent(new CustomEvent(type));
}
</script>
<button onclick={() => dispatch('decrement')}>减一</button>
<button onclick={() => dispatch('increment')}>加一</button>
使用这个自定义元素时,监听事件的方式跟原生 HTML 元素一样:
<!-- App.svelte -->
<script>
import './Stepper.svelte';
let count = $state(0);
</script>
<!-- 监听自定义元素的事件 -->
<my-stepper
ondecrement={() => count -= 1}
onincrement={() => count += 1}
></my-stepper>
<p>计数:{count}</p>
Note
$host()仅在组件被编译为自定义元素时可用。如果你在普通组件中调用它会报错。自定义元素是 Svelte 与原生 Web Components 交互的桥梁。
回调 Props vs Store vs Context
你可能会问:组件之间通信方式这么多,什么时候用回调 Props?
| 方式 | 适用场景 | 数据流向 |
|---|---|---|
| 回调 Props | 父子组件,事件通知 | 子 -> 父 |
bind: 绑定 | 父子组件,双向同步 | 父 <-> 子 |
| Store | 跨组件共享状态 | 任意方向 |
| Context API | 祖先到后代传值 | 祖先 -> 后代 |
简单来说,父给子传数据用 Props,子给父发通知用回调 Props,跨多层组件共享状态用 Store 或 Context。
本节回顾
- Svelte 5 废弃了
createEventDispatcher,用回调 Props 模式替代 - 回调 Props 就是把函数当作 Prop 传给子组件,子组件直接调用
- 事件转发通过
{...props}展开语法实现,所有事件属性自动转发 $host()rune 仅用于自定义元素场景,返回宿主 DOM 节点- 回调 Props 命名以
on开头,与原生事件属性风格保持一致