当前位置: 首页 > 工具软件 > Svelte > 使用案例 >

Svelte 官方入门教程(5)—— 事件

鞠隐水
2023-12-01

本章我们专门探讨 Svelte 事件处理。

1、DOM 事件

我们来写一个简单的程序,界面上展示鼠标当前所在的坐标:

<script>
  let m = { x: 0, y: 0 };

  function handleMousemove(event) {
    m.x = event.clientX;
    m.y = event.clientY;
  }
</script>

<style>
  div { width: 100%; height: 100%; }
</style>

<div>
  The mouse position is {m.x} x {m.y}
</div>

我们此前已经初步了解到,要在元素上监听任意事件,我们可以使用 on: 指令:

<div on:mousemove={handleMousemove}>
  The mouse position is {m.x} x {m.y}
</div>

2、内联事件

你也可以用内联的方式声明事件处理:

<div on:mousemove="{e => m = { x: e.clientX, y: e.clientY }}">
  The mouse position is {m.x} x {m.y}
</div>

属性值上的双引号其实是可选的,只是某些环境下有助于语法突出显示。

在一些框架中,出于性能原因,尤其是在循环内部,可能会建议你避免使用内联事件处理。不过这建议不适用 Svelte,无论你选择哪种形式,编译器都将始终会做正确的事情。

3、事件修饰符

DOM 事件处理程序允许使用修饰符来变更行为。例如,对某个处理程序使用once修饰符后,这个处理程序只会运行一次:

<script>
  function handleClick() {
    alert('no more alerts')
  }
</script>

<button on:click|once={handleClick}>Click me</button>

事件修饰符完整的列表:

  • preventDefault:在运行事件处理程序之前先调用 event.preventDefault() ,这对客户端在表单处理时就很有用。
  • stopPropagation:调用 event.stopPropagation() 停止事件冒泡,防止事件传播到下一个元素。
  • passive:改进触摸/滚轮事件的滚动性能(Svelte 默认会自动在安全的地方添加它)。
  • nonpassive:显式设置 passive: false
  • capture:在 捕获 阶段就触发处理程序,而非 冒泡 阶段(参阅 MDN 文档)
  • once:在运行一次事件处理后,立即将其移除。
  • self:仅当 event.target 是元素本身时才触发事件处理程序。
    修饰符支持链式使用,例如:on:click|once|capture={...}

4、组件事件

组件也可以发送(dispath)事件。为此,需要创建一个事件分发器(dispatcher)。

为了展示组件事件,需要先写一个组件,假设是 Inner.svelte:

Inner.svelte

<script>
  // setup code goes here

  function sayHello() {
  
  }
</script>

<button on:click={sayHello}>Click to say hello</button>

我们在 App.svelte 引入这个 Inner.svelte 组件:

App.svelte

<script>
  import Inner from './Inner.svelte';

  function handleMessage(event) {
    alert(event.detail.text);
  }
</script>

<Inner on:message={handleMessage}/>

你可以看到,在 App.svelte 中绑定了一个 on:message 自定义的事件名给 Inner,这说明 Inner 组件需要能够发送这个事件,handleMessage 才有可能被调用。

如何在 Inner 中对外发送一个自定义的事件?—— 这就需要用到事件分发器 EventDispatcher

尝试修改一下 Inner.svelte:

Inner.svelte

<script>
  import { createEventDispatcher } from 'svelte';

  const dispatch = createEventDispatcher();

  function sayHello() {
    dispatch('message', {
      text: 'Hello!'
    });
  }
</script>

<button on:click={sayHello}>Click to say hello</button>

createEventDispatcher 必须在组件实例化时就调用它来创建分发器,你无法在组件一些特殊位置中创建(例如利用 setTimeout 延迟几秒之后创建)。dispatch 将会关联到组件实例中。

注意:由于 on:message 指令,使得 App 组件得以监听由 Inner 组件发出的消息。

这个指令是一个以 on: 作为前缀的属性,后面跟着我们正在分发的事件名称(在当前例子叫 message)。

不过就算没有这个属性,消息仍然会被分发,只是应用不会有任何响应而已。你可以尝试删除 on:message,然后再点击按钮。

你也可以尝试修改事件的名称为其他任意名称。例如,在 Inner.svelte 中将 dispatch('message') 改为 dispatch('myevent'),然后在 App.svelte 中将属性 on:message 改为 on:myevent

5、事件转发

与DOM事件不同,组件事件不会 冒泡。 如果要在某个深层嵌套的组件上监听事件,则中间组件必须 转发 该事件。

在这种情况下,我们具有与上一节相同的 App.svelteInner.svelte,不过现在还需创建一个 Outer.svelte,并且在 Outer.svelte 组件中添加 <Inner/>

Outer.svelte

<script>
  import Inner from './Inner.svelte';
</script>

<Inner/>

解决这个问题的其中一种方案,是在 Outer.svelte 添加 createEventDispatcher,然后监听 message 事件,并为其创建处理程序:

Outer.svelte

<script>
  import Inner from './Inner.svelte';
  import { createEventDispatcher } from 'svelte';

  const dispatch = createEventDispatcher();

  function forward(event) {
    dispatch('message', event.detail);
  }
</script>

<Inner on:message={forward}/>

不过如果层次过深,这么一层一层地转发枯燥乏味,Svelte 有一种等效的速写方式:on:message事件指令不写其值,就代表“转发所有message事件”,Svelte 自动生成与上方相同的代码替你包圆了:

<script>
  import Inner from './Inner.svelte';
</script>

<Inner on:message/>

6、DOM 事件转发

上一节学到的组件自定义事件转发,同样适用于 DOM。

下方包装了一个 CustomButton.svelte 闪亮按钮组件:

CustomButton.svelte

<style>
  button {
    height: 4rem;
    width: 8rem;
    background-color: #aaa;
    border-color: #f1c40f;
    color: #f1c40f;
    font-size: 1.25rem;
    background-image: linear-gradient(45deg, #f1c40f 50%, transparent 50%);
    background-position: 100%;
    background-size: 400%;
    transition: background 300ms ease-in-out;
  }

  button:hover {
    background-position: 0;
    color: #aaa;
  }
</style>

<button>Click me</button>

App.svelte

<script>
  import CustomButton from './CustomButton.svelte';

  function handleClick() {
    alert('clicked');
  }
</script>

<CustomButton on:click={handleClick}/>

作为一个纯粹的通用按钮的封装,CustomButton.svelte 不处理具体点击事件,而应该将事件发送给使用端去处理。

例如,当 <CustomButton> 的点击的事件,我们期望可以收到通知,为此,只需转发 CustomButton.svelte 中的 <button> 元素的 click 事件即可。

CustomButton.svelte

......

<button on:click>Click me</button>

如何响应这个点击事件,交由 App.svelte 接收到 click 事件后自行处理。

总结

本章探讨了 Svelte 事件的全部内容,最简单也是最常用的 DOM 事件绑定。

通过使用 on: 指令,例如 on:click,你可以直接内联一个函数的形式 on:click={ () => ... },又或者将函数提到外部的形式 on:click={ handler }。两者的结果没有什么区别。

它同时也支持绑定组件的自定义事件,组件需要借助 createEventDispatcher() 方法将事件转发到使用端。

如果你正在编写一个 Svelte 组件库,那么事件转发是组件库创作者的必备工具箱,组件库里的组件一般不处理事件响应,而是应该将这个响应移交使用端处理。

不得不说,对比起 Vue 的写法,Vue 支持 @ 符号声明事件更为简洁一些,例如:

<button @click>Click me</button>
 类似资料: