Skip to content

事件处理

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 4Svelte 5
on:clickonclick
on:inputoninput
on:submitonsubmit
on:keydownonkeydown
on:mouseenteronmouseenter
on:changeonchange

规则很简单:把 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 的 preventDefaultstopPropagation 修饰符(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|preventDefaulte.preventDefault()
on:click|stopPropagatione.stopPropagation()
on:click|selfif (e.target === e.currentTarget)
on:keydown|enterif (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 类型

下一步:把所有概念串起来 —— 综合实战示例

基于 VitePress 构建 · 示例使用 Svelte 5.57