WebSocketではないTurbo Streamsのsourceを作って遊ぶ
2020年の年末にBasecampからHotwireが発表されました。
そのなかのTurbo Streamsですが、sourceはWebSocketである必要はなく、自分でsourceを作ることもできます。以下では簡単なsourceを作って遊んでみます。
以下で使っているバージョンは@hotwired/turbo
の7.0.0-beta.3
です。
Turbo Streamsが要求していることは何なのか
特にドキュメントで明示されているわけではないですが、ActionCable
をsourceとして利用しているturobo-railsのTurboCableStreamSourceElementが参考になります。
sourceは@hotwired/turbo
のconnectStreamSource
で利用することができますが、きちんと型の情報も定義されています。
connectStreamSource(source: StreamSource): void;
でStreamSource
はといういと、
type StreamSource = {
addEventListener(type: "message", listener: (event: MessageEvent) => void, options?: boolean | AddEventListenerOptions): void
removeEventListener(type: "message", listener: (event: MessageEvent) => void, options?: boolean | EventListenerOptions): void
}
MessageEventをdispatchするEventTargetのようなものであることを要求しているようです。
MessageEvent
の中身としてはdata
にturbo-stream
を入れるとよさそうです。turbo-stream
はほぼHTMLのようなものです。
時刻を更新するだけのsourceを作ってみる。
では実際に作っていきましょう。
div#timer
を作って、それを毎秒更新するためのsourceを作ってみます。
EventTarget
的なものを作ってもいいですが、turbo-railsを参考にcustom elementsを作ってサボります。
Turbo Streamsのactionにはappend
, prepend
, replace
, remove
がありますが、ここではreplace
を使って、ただ書き換えるだけにします。なのでMessageEvent
のdata
としては
<turbo-stream action="replace" target="timer">
<template>
<div id="timer">
${new Date().toISOString()}
</div>
</template>
</turbo-stream>
を吐き出します。
以上のことを素直に実装すると、
import { connectStreamSource, disconnectStreamSource } from "@hotwired/turbo"
class TickElement extends HTMLElement {
timerId?: number;
connectedCallback() {
connectStreamSource(this);
this.timerId = setInterval(this.dispatchMessageEvent.bind(this), 1000);
}
disconnectedCallback() {
disconnectStreamSource(this);
if (this.timerId) clearInterval(this.timerId);
}
dispatchMessageEvent() {
const data = `
<turbo-stream action="replace" target="timer">
<template>
<div id="timer">
${new Date().toISOString()}
</div>
</template>
</turbo-stream>
`
const event = new MessageEvent("message", { data });
this.dispatchEvent(event);
}
}
customElements.define("tick-source", TickElement);
といった感じになります。これをHTMLから
<div id="timer"></div>
<tick-source />
と利用すれば…
自分で作ったsourceで更新することができました!
最後に
一応成果物は
にあります。Hotwireの旨みはサーバサイドのテンプレートを使い回すことで楽ができることなので、今回作ったsourceは実用的とは言えません。しかしsourceを書きさえすれば、Server Sent EventsでもWeb Pushでも、ポーリングだろうとTurbo Streamsで利用することができるということです。意外と簡単に書けるので色々やりようがありそうですね。
Discussion