🫥

Svelte と Skeleton で作る ChatGPT アプリ

2023/07/03に公開

Svelte

https://svelte.dev/

日本語版: https://svelte.jp/

'仮想DOMは速い'という神話を完全に終わりにしよう

私が Svelte に興味を持つキッカケになった記事です。

https://svelte.dev/blog/virtual-dom-is-pure-overhead

日本語訳: 仮想DOMは純粋なオーバーヘッド

Svelte の特徴

  1. Write less code ... 少ないコード量で記述できる
  2. No Virtual DOM ... 仮想DOMを使用しない
  3. Truly reactive ... 真にリアクティブ

先生に聞いてみました。

Svelte は、ウェブアプリケーションの開発に使用される JavaScript フレームワークです。

Svelte の採用メリットは以下のような特徴によってもたらされます:

  1. フレームワーク自体のサイズが小さい: Svelte は、コンパイル時にフレームワークのコードを最適化し、生成されるバンドルサイズを小さくします。これにより、ユーザーがアプリケーションを高速にロードし、レスポンシブなユーザーエクスペリエンスを提供できます。

  2. リアクティブなコンポーネントモデル: Svelte の特徴的な機能は、リアクティブなコンポーネントモデルです。変数や状態の変更を監視し、自動的に DOM の更新を行います。これにより、手動で DOM 操作を行う必要がなくなり、開発者はコンポーネントのロジックに集中できます。

  3. 仮想 DOM の不要: 一般的な JavaScript フレームワークでは、仮想 DOM (Virtual DOM) を使用してUIの変更を追跡し、効率的に再描画します。Svelte では、コンパイル時にコンポーネント間の差分を計算し、必要な DOM 更新を生成するため、仮想DOMのオーバーヘッドがありません。これにより、高速なレンダリングパフォーマンスが実現されます。

  4. 学習コストが低い: Svelte は、独自のシンプルな構文を持っています。既存の HTML、CSS、JavaScript の知識を活用しながら学習できるため、学習コストが比較的低くなります。また、他のフレームワークに比べて API や概念がシンプルなため、新しい開発者がプロジェクトに参加しやすくなります。

  5. パフォーマンスの最適化が容易: Svelte は、コンパイル時に最適化されたコードを生成します。不要な処理や再描画を削減するため、高速な実行性能を提供します。また、Svelte のコンポーネントはグラフィックス API である WebGL とシームレスに連携できるため、リッチな視覚的体験を提供するアプリケーションの開発が可能です。

これらの要素により、Svelte はパフォーマンスの高いウェブアプリケーションの開発に適しています。ただし、プロジェクトの要件や開発チームのスキルセットによって最適なフレームワークの選択は異なる場合もありますので、注意が必要です。

チュートリアル

https://svelte.jp/tutorial/basics

Web 上で体験することができるので、気軽にトライすることができます😉

チュートリアルを進めながら、気になるところなどはスクラップにまとめました。

https://zenn.dev/codemountains/scraps/4b161e554437e9

ChatGPT アプリを作る

チュートリアルを進めていたのですが、ちょっと飽きてしまい...
何か動くものでも作ろうと思い、OpenAI API を使ってチャットアプリを作ってみました!

https://github.com/codemountains/svelte-openai-app

注意

openai という npm パッケージを使用しています。

Important note: this library is meant for server-side usage only, as using it in client-side browser code will expose your secret API key. See here for more details.

本来はサーバーサイドでの使用を想定しているため、注意してください。

実装ポイント

openai ライブラリ

環境変数に取り扱いがまだ理解しきれていないため、一旦 public として扱うことにしました。

lib フォルダに作成したが、これが正解なのか不明です。
プロジェクトのディレクトリ構成も学習する必要があります...🙏

src/lib/openai.ts
import { Configuration, OpenAIApi } from 'openai';
import { PUBLIC_OPENAI_ORGANIZATION_ID, PUBLIC_OPENAI_API_KEY } from '$env/static/public';

const configuration = new Configuration({
    organization: PUBLIC_OPENAI_ORGANIZATION_ID,
    apiKey: PUBLIC_OPENAI_API_KEY,
});

const openai = new OpenAIApi(configuration);

export default openai;

OpenAI API の実行は以下のように実装できます。

const res = await openai.createChatCompletion({
    model: "gpt-3.5-turbo",
    messages: messageFeeds.map(m => { return { role: "user", content: m.message as string }}),
});

メッセージは配列である点がポイントで、一連のやり取りを送る必要があります。

配列に 1 つのメッセージのみだと常に単発の返答のみが返ってくるので、会話として成り立ちません。

Skeleton

今回は Svelte で使用できる UI ツールキットである Skeleton を使用してみました💀

なお、使用にあたって、Tailwind CSS の知見も必要になってきます。

https://www.skeleton.dev/

手動で後からも導入できますが、以下のコマンドでプロジェクト作成のタイミングから導入すると簡単でした。

npm create skeleton-app@latest my-skeleton-app

最初にテーマも選べて、良い感じの配色にしてくれます。

しかも、LightSwitch コンポーネントで簡単に切り替えも実装できる!

<LightSwitch />

メインとなるチャット画面については、以下を参考にして実装しました。

https://www.skeleton.dev/elements/chat

メッセージの表示部分は以下です。

src/routes/+page.svelte
<section class="w-full h-full space-y-4">
    {#each messageFeeds as bubble, i}
        {#if bubble.host === true}
            <!-- Your Message Bubble -->
            <div class="grid grid-cols-[1fr_auto] gap-2">
                <div class="card p-4 rounded-tr-none space-y-2 variant-soft-primary">
                    <header class="flex justify-between items-center">
                        <p class="font-bold">You</p>
                        <small class="opacity-50">{bubble.timestamp}</small>
                    </header>
                    <p>{bubble.message}</p>
                </div>
                <Avatar initials="Y" width="w-12" />
            </div>
        {:else}
            <!-- OpenAI Message Bubble -->
            <div class="grid grid-cols-[auto_1fr] gap-2">
                <Avatar initials="AI" width="w-12" />
                <div class="card p-4 variant-soft rounded-tl-none space-y-2">
                    <header class="flex justify-between items-center">
                        <p class="font-bold">OpenAI</p>
                        <small class="opacity-50">{bubble.timestamp}</small>
                    </header>
                    <p>{bubble.message}</p>
                </div>
            </div>
        {/if}
    {/each}
</section>

また、コンテンツの読み込み中に表示できる「スケルトン」プレースホルダーも簡単に実装できました。

<div class="placeholder animate-pulse" />

課題・今後の展望

ディレクトリ構成の見直しとコンポーネント分割

初見ということで、ディレクトリ構成の見直しとコンポーネント分割まで手が回りませんでした。

画面下部への自動スクロール

Skeleton のチャット を参考に自動スクロールを実装しているのだが、機能していないので修正したいです。

マークダウンへの対応

サンプルコードを聞くと、返答にコードブロックが含まれることがあります。

console.log("ぜひとも、このように表示したい!");
console.log("コピーもできるようにしたい!");

型推論

サンプルコードが JS なことが多く、そのままにしてある部分があるので改修したいです。

AI としりとりしてみた

しりとりしてみました!

... ん?!

まとめ

往生際が悪い... 勝つまでやめないタイプだ...

コラボスタイル Developers

Discussion