事件处理
Svelte 5 对事件处理做了重要简化:事件就是属性。不再需要 on:click 指令,也不用 createEventDispatcher。
DOM 事件:小写属性
在 Svelte 5 里,监听 DOM 事件直接用小写的 on<事件名> 属性:
svelte
<script>
let count = $state(0);
</script>
<!-- Svelte 5:onclick -->
<button onclick={() => count++}>点了 {count} 次</button>对比 Svelte 4 的写法(已过时):
svelte
<!-- Svelte 4:on:click(不再推荐) -->
<button on:click={() => count++}>点了 {count} 次</button>常见事件属性
| Svelte 4 | Svelte 5 |
|---|---|
on:click | onclick |
on:input | oninput |
on:submit | onsubmit |
on:keydown | onkeydown |
on:mouseenter | onmouseenter |
on:change | onchange |
规则很简单:把 on: 去掉,事件名改成全小写连写。
事件对象
处理函数会收到原生事件对象:
svelte
<script>
function handleClick(event) {
console.log('点击位置', event.clientX, event.clientY);
}
</script>
<button onclick={handleClick}>点我</button>内联写法也可以:
svelte
<button onclick={(e) => console.log(e.clientX)}>点我</button>阻止默认行为与冒泡
Svelte 4 的 preventDefault、stopPropagation 修饰符(on:submit|preventDefault)在 Svelte 5 里直接在处理函数中调用原生方法:
svelte
<form onsubmit={(e) => {
e.preventDefault(); // 取代 |preventDefault
e.stopPropagation(); // 取代 |stopPropagation
handleSubmit();
}}>
<button type="submit">提交</button>
</form>更符合直觉
这其实是好事:处理逻辑就是标准 JavaScript,没有框架特有的修饰符语法需要记。
组件事件:回调即属性
这是 Svelte 5 最大的观念转变之一。子组件不再「派发」事件,而是接收回调函数作为 props:
svelte
<!-- 子组件 Button.svelte -->
<script>
// 事件就是普通的函数 prop
let { onclick } = $props();
</script>
<button {onclick}>点我</button>svelte
<!-- 父组件 -->
<script>
import Button from './Button.svelte';
</script>
<Button onclick={() => alert('被点击了!')} />自定义事件同理 —— 子组件接收一个回调,在合适的时机调用它:
svelte
<!-- 子组件 Search.svelte -->
<script>
let { onsearch } = $props(); // 自定义「事件」= 回调 prop
let query = $state('');
</script>
<input bind:value={query} />
<button onclick={() => onsearch(query)}>搜索</button>svelte
<!-- 父组件 -->
<Search onsearch={(q) => console.log('用户搜索了', q)} />告别 createEventDispatcher
Svelte 4 里派发组件事件要用 createEventDispatcher:
svelte
<!-- Svelte 4(已过时) -->
<script>
import { createEventDispatcher } from 'svelte';
const dispatch = createEventDispatcher();
</script>
<button on:click={() => dispatch('notify', { text: 'hi' })}>通知</button>Svelte 5 不再需要它。直接传回调函数即可,好处是:
- 类型安全:回调函数的参数有完整类型
- 更直观:就是普通函数调用,没有额外抽象
- 可组合:回调可以层层传递、包裹
事件修饰符的替代
Svelte 4 的一些修饰符在 Svelte 5 里用原生方式处理:
| Svelte 4 修饰符 | Svelte 5 替代 |
|---|---|
on:click|once | 在处理函数里手动移除,或用状态标记只执行一次 |
on:click|preventDefault | e.preventDefault() |
on:click|stopPropagation | e.stopPropagation() |
on:click|self | if (e.target === e.currentTarget) |
on:keydown|enter | if (e.key === 'Enter') |
试一试
下面的示例完整演示了「回调即属性」:子组件 CustomEvents 通过 onnotify / onreset 回调通知父组件,父组件把收到的消息记录到日志里。点按钮,观察父组件的日志变化:
组件事件 · 回调即属性Svelte 5 · Live
子组件源码:
svelte
<script>
let { onnotify = () => {}, onreset = () => {} } = $props();
let clicks = $state(0);
function handleClick() {
clicks += 1;
onnotify(`被点击了 ${clicks} 次`); // 调用回调 = 派发事件
}
</script>
<button onclick={handleClick}>点我触发事件</button>父组件源码:
svelte
<script>
import CustomEvents from './CustomEvents.svelte';
let log = $state([]);
function handleNotify(message) {
log = [{ id: crypto.randomUUID(), message }, ...log].slice(0, 5);
}
</script>
<CustomEvents onnotify={handleNotify} onreset={() => (log = [])} />小结
| 要点 | 说明 |
|---|---|
onclick={fn} | DOM 事件用小写属性(取代 on:click) |
e.preventDefault() | 用原生方法取代修饰符 |
| 回调即属性 | 组件事件 = 传给子组件的函数 prop |
| 移除 | 不再需要 createEventDispatcher |
| 类型安全 | 回调参数有完整 TS 类型 |
下一步:把所有概念串起来 —— 综合实战示例。