Svelte + SvelteKit + WebSocket でチャットアプリ
#おべんきょ #nodejs #typescript #svelte
久々にfrontendのおべんきょ Svelteで掲示板 の続き
前の記事でSvelte + SvelteKitをふんわり体験したから、
次はWebSocketを使ったリアルタイムな更新をやってみたい。
ってなわけで、チャットアプリの記事があったのでそれを参考にやってみる。
参考にしたのは下記の記事
Live-Chat with SvelteKit and SocketIO
実際に作ったコードは下記
tsuchinaga / sveltekit-chat-app · GitLab
そもそもWebsoket対応してないの?
Sveltekitくらい便利そうなフレームワークならWebsocket対応してそうなもんやのに。
デフォで対応していなくても、簡単に実装できる方法を提供してるとか、そういうのを期待してちょっと調べてみる。
Native support for web sockets · Issue #1491 · sveltejs/kit · GitHub
環境構築
前の記事でやったので、今回は割愛
プロジェクト作成
ここは前回と同じ流れやし流していく
$ npm init svelte@latest chat-app
create-svelte version 5.0.2
┌ Welcome to SvelteKit!
│
◇ Which Svelte app template?
│ Skeleton project
│
◇ Add type checking with TypeScript?
│ Yes, using TypeScript syntax
│
◇ Select additional options (use arrow keys/space bar)
│ Add ESLint for code linting
│
└ Your project is ready!
✔ Typescript
Inside Svelte components, use <script lang="ts">
✔ ESLint
https://github.com/sveltejs/eslint-plugin-svelte
Install community-maintained integrations:
https://github.com/svelte-add/svelte-add
Next steps:
1: cd chat-app
2: npm install (or pnpm install, etc)
3: git init && git add -A && git commit -m "Initial commit" (optional)
4: npm run dev -- --open
To close the dev server, hit Ctrl-C
Stuck? Visit us at https://svelte.dev/chat
$ cd chat-app
$ npm install
added 219 packages, and audited 220 packages in 26s
44 packages are looking for funding
run npm fund for details
found 0 vulnerabilities
一応ここでプロジェクト作成としてコミットを入れといた
$ npm run dev -- --open
> chat-app@0.0.1 dev
> vite dev --open
Forced re-optimization of dependencies
VITE v4.4.2 ready in 646 ms
➜ Local: http://localhost:5173/
➜ Network: use --host to expose
➜ press h to show help
ってなわけで空っぽのプロジェクトは作れました。
参考にしている記事では、CSSにTailwindを利用しているみたい
あとから調べて入れてみよう
SvelteKit with Tailwind CSS v3
Socket.ioのインストール
websocketのサーバとクライアントとして、Socket.ioとやらを使ってるみたい。
$ npm install socket.io socket.io-client
added 22 packages, and audited 242 packages in 5s
44 packages are looking for funding
run npm fund for details
found 0 vulnerabilities
サーバサイド
SvelteKitはSSR含めサーバサイドとフロントサイドの開発のフレームワーク。
websocketはサーバとクライアントがリアルタイムに通信するための方法。
ってことで、ひとまずサーバサイドの準備を進める
Socket.ioの設定
あんまりよくわかってないけど、SvelteKitで立てるViteサーバでwebsocketのサーバも管理するために、
sveltekitの設定ファイルのviteの項目に設定するって感じなんかな?
sveltekit-socket-io の中身自体は比較的単純な処理なんやろうなーという感じ。
接続があったらユーザー名設定してメッセージを受け取るところを作る。
code:svelte.config.js
import adapter from '@sveltejs/adapter-auto';
import {vitePreprocess} from '@sveltejs/kit/vite';
import {Server} from 'socket.io'
/** @type {import('@sveltejs/kit').Config} */
const config = {
preprocess: vitePreprocess(),
kit: {
adapter: adapter(),
vite: {
plugins: [{
name: 'sveltekit-socket-io',
configureServer(server) {
const io = new Server(server.httpServer)
io.on('connection', (socket) => {
let username = User ${Math.round(Math.random() * 999999)}
socket.emit('name', username)
socket.on('message', (message) => {
io.emit('message', {
from: username,
message: message,
time: new Date().toLocaleString()
})
})
})
console.log('SocketIO injected')
}
}]
}
}
};
export default config;
さて、動くかな?
$ npm run dev -- --open
> chat-app@0.0.1 dev
> vite dev --open
error when starting dev server:
Error: Unexpected option config.kit.vite
at file:///D:/projects/tsuchinaga/sveltekit-chat-app/chat-app/node_modules/@sveltejs/kit/src/core/config/options.js:315:12
at file:///D:/projects/tsuchinaga/sveltekit-chat-app/chat-app/node_modules/@sveltejs/kit/src/core/config/options.js:322:18
at validate_config (file:///D:/projects/tsuchinaga/sveltekit-chat-app/chat-app/node_modules/@sveltejs/kit/src/core/config/index.js:108:9)
at process_config (file:///D:/projects/tsuchinaga/sveltekit-chat-app/chat-app/node_modules/@sveltejs/kit/src/core/config/index.js:80:20)
at load_config (file:///D:/projects/tsuchinaga/sveltekit-chat-app/chat-app/node_modules/@sveltejs/kit/src/core/config/index.js:72:9)
at async sveltekit (file:///D:/projects/tsuchinaga/sveltekit-chat-app/chat-app/node_modules/@sveltejs/kit/src/exports/vite/index.js:119:24)
at async Promise.all (index 0)
at async asyncFlatten (file:///D:/projects/tsuchinaga/sveltekit-chat-app/chat-app/node_modules/vite/dist/node/chunks/dep-8609dc5d.js:12652:16)
at async resolveConfig (file:///D:/projects/tsuchinaga/sveltekit-chat-app/chat-app/node_modules/vite/dist/node/chunks/dep-8609dc5d.js:65581:29)
at async _createServer (file:///D:/projects/tsuchinaga/sveltekit-chat-app/chat-app/node_modules/vite/dist/node/chunks/dep-8609dc5d.js:64822:20)
エラー!!!
ちょっと違うgithubのissueからやけど参考になりそうなのがあった。
"config.kit.vite has been removed — use vite.config.js instead" error · Issue #48 · svelteness/kit-docs · GitHub
viteの設定は config.svelte.js に書くのではなく、 vite.config.ts に書くみたい。
ってことで、 svelte.config.js は元に戻して、vite.config.ts に書き込む。
あと、他記事も参考にして書きやすそうな方法に微調整
code:vite.config.ts
import {sveltekit} from '@sveltejs/kit/vite';
import {defineConfig} from 'vite';
import {Server} from 'socket.io'
const webSocketServer = {
name: 'webSocketServer',
configureServer(server: any) {
const io = new Server(server.httpServer)
io.on('connection', (socket) => {
const username = User ${Math.round(Math.random() * 999_999)}
socket.emit('name', username)
socket.on('message', (message) => {
io.emit('message', {
from: username,
message: message,
time: new Date().toLocaleString(),
})
})
})
console.log('SocketIO injected');
}
}
export default defineConfig({
plugins: [
sveltekit(),
webSocketServer,
]
});
ってことで再度起動確認
$ npm run dev -- --open
> chat-app@0.0.1 dev
> vite dev --open
Forced re-optimization of dependencies
VITE v4.4.2 ready in 684 ms
➜ Local: http://localhost:5173/
➜ Network: use --host to expose
➜ press h to show help
よしよし。
実際に動かすのはもうちょい後になるから、とりあえず記事の続きを試していくぞー
クライアントサイド
サーバサイドはざっくりできたので、お次はクライアントサイド。
クライアント側のSocket.ioの実装
どの画面から使うかはいったん置いといて、クライアント側のどこからでもSocket.ioを呼べるように定義しておく
code:src/lib/realtime.ts
import ioClient from 'socket.io-client'
const ENDPOINT = 'http://localhost:3000'
const socket = ioClient(ENDPOINT)
export const io = socket
画面
デザインとか全部なしで、いったん必要な項目を置くだけ
記事では src/routes/index.svelte になってたけど、何でそうなるのか分からなかったから +page.svelte にした。
後から理由が分かったら修正するかも。
code:src/routes/+page.svelte
<script lang="ts">
let textField = ''
let userName = ''
let messages = []
function sendMessage() {
}
</script>
<div>
<div>
<header>
<span>My Chat App</span>
<span>{userName}</span>
</header>
<div>
{#each messages as message}
<div>
<span>
<b>{message.from}</b>
<i>{message.time}</i>
</span>
{message.message}
</div>
{/each}
</div>
<form on:submit|preventDefault={sendMessage}>
<input type="text" bind:value={textField}>
<button type="submit">Send</button>
</form>
</div>
</div>
メッセージ送信
+page.svelte の sendMessage 関数の中身を作っていく。
具体的には、サーバに向かってメッセージを投げつける感じ。
code:src/routes/+page.svelte
<script lang="ts">
import {io} from "$lib/realtime"
let textField = ''
let userName = ''
let messages = []
function sendMessage() {
const message = textField.trim()
if (!message) return
textField = ''
io.emit('message', message)
}
</script>
// ...
これでコメントを送信すると、 vite.config.ts で設定した message イベントが発火する。
メッセージ受信
+page.svelte にメッセージ受信時の処理を書いておく。
タイミングは描画直後なので、 onMount に書くことになる。
code:src/routes/+page.svelte
<script lang="ts">
import {io} from "$lib/realtime"
import {onMount} from "svelte";
let textField = ''
let userName = ''
let messages = []
onMount(() => {
io.on('message', message => {
messages = ...messages, message
})
io.on('name', name => {
userName = name
})
})
function sendMessage() {
const message = textField.trim()
if (!message) return
textField = ''
io.emit('message', message)
}
</script>
// ...
これでクライアント側もかんせーい。
動かしてみよう!
$ npm run dev -- --open
しても、たぶんそのままじゃ動かない。
run devした際に適当なポートでwebサーバが立ち上がるので、
src/lib/realtime.ts の port 部分を対象のサーバに書き換えてあげないといけない。
上記書換だけやっておけば、ブラウザで複数タブを開いて、
メッセージを送ってみれば両方に新しいメッセージが届くようになる。
素晴らしい!
参考
Native support for web sockets · Issue #1491 · sveltejs/kit · GitHub
Using WebSockets With SvelteKit
Live-Chat with SvelteKit and SocketIO
SvelteKit with Tailwind CSS v3
Socket.IO
"config.kit.vite has been removed — use vite.config.js instead" error · Issue #48 · svelteness/kit-docs · GitHub
更新履歴
#2023/07/13 かきおわり
#2023/07/11 かきはじめ