這是〈Svelte 從小白到入門〉系列的第三集,各集連結如下:


在第一集我們談到 reactivity 時也簡短的用到了 Svelte 的事件綁定 on:click 的語法,這集我們要探索更多 Svelte 的事件機制。

事件

能ㄤㄤㄤ的不只小叮噹,Svelte 也可以ㄤㄤㄤ。

下面是 on:mousemove 的示例,在紅色<div>,我們呼叫 handleMousemove() 給出滑鼠座標,相當直白(直白是種高尚的情操),而在橘色<div>,我們也可以改用在 HTML 用 inline 的方式呼叫一般函式或匿名函式:

事件修飾器

事件也有裝飾器,Svelte 原文稱為 modifier,而不是 decorator,不論名字為何,他們的角色是相同的—改變對象原有的行為。

下面這個例子用上了 once 裝飾器,所以點擊的行為只會觸發一次事件函式:

注意裝飾器的語法 on:click|once 中間有個管道符號 | 連接事件與修飾器。

至此我們已經看過 Svelte 兩種風格的修飾器語法:

  • @html string@html 讓字串不做跳脫處理,而 @html 這種有 @ 符號的語法看起來像是 Python 風格的裝飾器語法。
  • on:click|onceonce 讓點擊事件只發生一次,而 |once 這種有 | 管線符號的語法看起來像是 Unix 系的管道語法

同時摻用兩種風格的語法,讓 Svelte 看起來是混亂的,如果把 @html string 改以 string|html,感覺起來整體風格會更為一致。

不過木已成舟,讓我們繼續跟著遊戲規則玩下去。

既有的事件修飾器如下,很偷懶的剪下貼上原文不解釋:

  • preventDefault - calls event.preventDefault() before running the handler. Useful for client-side form handling, for example.
  • stopPropagation - calls event.stopPropagation(), preventing the event reaching the next element.
  • passive - improves scrolling performance on touch / wheel events (Svelte will add it automatically where it’s safe to do so).
  • nonpassive - explicitly set passive: false.
  • capture - fires the handler during the capture phase instead of the bubbling phase (MDN docs)
  • once - remove the handler after the first time it runs.
  • self - only trigger handler if event.target is the element itself.
  • trusted - only trigger handler if event.isTrusted is true. I.e. if the event is triggered by a user action.

事件修飾器也可以像 Unix 管道一樣,串在一起:on:click|once|capture={...}

事件分派(Dispatch)與轉發(Forwarding)

Svelte 元件之間可以分派事件,在下面的示例裡,按鈕發生的事情如下:

  1. 按下 Inner 元件的按鈕
  2. 點擊觸發 Inner 按鈕的 on:click={sayHello}
  3. sayHello() 內的 dispatch('message') 又觸發父元件 App 的 message 事件
  4. message 事件又觸發 handleMessage()
  5. handleMessage() 內執行了 alert() 跳出提示框

這整個分派的機制得以實現,是靠 Svelte 提供的 createEventDispatcher(),我們用它創建一個 dispatch 實體,供派送事件之用。

注意到父元件 App 內的這行:

<Inner on:message="{handleMessage}"/>

這邊是調用了 Inner 元件,不過 message 事件是發生在在 App 元件上的,這是 on: 事件綁定的語法,並非 props 的語法,雖然兩者長的有那麼一點像…。

基於以上的概念,我們讓它複雜一點,在 App 和 Inner 中間夾一層 Outer 元件,用 App 包 Outer、Outer 包 Inner 的方式形成巢狀引用,因為 Svelte 的事件沒有「冒泡」的機制,所以前一個例子的 Inner 丟出的 message 事件被 Outer 收到就停了,並不會自然而然的再往外丟給 App,因此在不改程式碼的情況下,按鈕自然也不會有反應的。

想讓事件往外轉發,得利用 Svelte 的轉發機制(語法糖),在 Outer 元件內,調用 Inner 之處,加上 on:message 的綁定語法,Svelte 就知道「哦,要轉發出去」,看起來很簡單,但個人認為頗不自然…:

事件轉發的機制也可以套用在原生的 DOM 物件上,按鈕加個 on:click 就會把點擊事件往外轉發出去了:

小結

整理本集重點:

  • 事件以 on: 語法綁定
  • 可以在 Svelte 的 HTML 元素內寫 inline 的函式
  • 事件可以搭配管道符號 | 接上修飾器,並且可以串接多個修飾器
  • Svelte 提供了用 createEventDispatcher() 建立的實例做事件分派的機制
  • Svelte 的事件不會冒泡,要轉送事件得用像 on:click 這樣的語句向外轉送點擊事件

原本一集可以寫好幾個主題,現在一集只能寫一個主題,究竟會不會斷尾呢?讓我們看下去…。

(待續)