【Svelte】SvelteKitを使ってサンプルコードを書いてみる

Svelteを触ったからには「SvelteKit」を触らないといけない気がしたので、
DEMOプロジェクトにあるカウンターアプリだけを写経してみました。

プロジェクトを作成する

プロジェクト作成
npm create svelte@latest sveltekit-counter

? Which Svelte app template? 
> Skeleton project

? Add type checking with TypeScript?
> Yes, using TypeScript syntax

# それ以降の質問はお好きにどうぞ。
モジュールインストール
cd sveltekit-counter
npm install
実行してみる
npm run dev
ターミナルにURLが出力されているので、そこにアクセスし、
以下の画面が表示されればOKです。

プロジェクト構成を確認してみる

コマンドで作成されたプロジェクトファイルは以下の通りです。
今回触るのはsrcディレクトリ内だけですが、
プロジェクト構成の詳細が気になる方はこちらのページを参照してください。

カウンターアプリを作ってみる

今回追加・修正するファイルは以下の通りです。
ファイル名の先頭に「+」があり、ん??と思うかもしれませんが、
間違いではありません。
「+」の接頭辞が付いているファイルはルートファイルと呼ばれ、
route内のディレクトリには1つ以上のルートファイルを格納します。 ルートファイルについて詳しく知りたい方は、こちらのページを参照してください。

初期表示される画面の内容をいじってみる

まずは、初期表示されている画面の内容を修正してみましょう。
+page.svelte
<h1>Hello, world.</h1>
保存後に画面を確認すると、「Hello, world.」と表示されているはずです。

カウンター用のコードを書く

routesディレクトリの中にファイル「Counter.svelte」を作成します。
Counter.svelt
<script lang="ts">
    let count = 0;
</script>

<div class="counter">
    <strong>{count}</strong>
</div>
作成したConterコンポーネントを呼び出してみましょう。
+page.svelte
<script>
    import Counter from './Counter.svelte';
</script>

<Counter />
画面に「0」と表示されていればOKです。

カウントアップ・ダウン用のボタンを追加する

「+」「ー」ボタンを追加し、「count」の値を変更させましょう。
Counter.svelte
<button on:click={() => (count -= 1)}>
    <svg aria-hidden="true" viewBox="0 0 1 1">
        <path d="M0,0.5 L1,0.5" />
    </svg>
</button>
<strong>{count}</strong>
<button on:click={() => (count += 1)}>
    <svg aria-hidden="true" viewBox="0 0 1 1">
        <path d="M0,0.5 L1,0.5 M0.5,0 L0.5,1" />
    </svg>
</button>

<style>
    .counter {
        display: flex;
        margin: 1rem 0;
    }

    .counter button {
        width: 2em;
        padding: 0;
        display: flex;
        align-items: center;
        justify-content: center;
        border: 0;
        background-color: transparent;
        touch-action: manipulation;
        font-size: 2rem;
    }

    svg {
        width: 25%;
        height: 25%;
    }

    path {
        vector-effect: non-scaling-stroke;
        stroke-width: 2px;
        stroke: #444;
    }
</style>
「ー」「+」ボタンをクリックして、カウントが変わればOKです。

アニメーションをつける

springを利用してカウンターのように数字がスクロールして表示させてみます。
カウンターのイメージ
コードは以下の通りです。
Counter.svelte
<script lang="ts">
    import { spring } from 'svelte/motion';

    let count = 0;
    const displayed_count = spring();

    $: displayed_count.set(count);
    $: offset = modulo($displayed_count, 1);

    function modulo(n: number, m: number) {
        return ((n % m) + m) % m;
    }
</script>

<div class="counter">
    <button on:click={() => (count -= 1)}>
        <svg aria-hidden="true" viewBox="0 0 1 1">
            <path d="M0,0.5 L1,0.5" />
        </svg>
    </button>
    <div class="counter-viewport">
        <div class="counter-digits" style="transform: translate(0, {100 * offset}%)">
            <strong class="hidden" aria-hidden="true">{Math.floor($displayed_count + 1)}</strong>
            <strong>{Math.floor($displayed_count)}</strong>
        </div>
    </div>
    <button on:click={() => (count += 1)}>
        <svg aria-hidden="true" viewBox="0 0 1 1">
            <path d="M0,0.5 L1,0.5 M0.5,0 L0.5,1" />
        </svg>
    </button>
</div>

<style>
    .counter {
        display: flex;
        margin: 1rem 0;
    }

    .counter button {
        width: 2em;
        padding: 0;
        display: flex;
        align-items: center;
        justify-content: center;
        border: 0;
        background-color: transparent;
        touch-action: manipulation;
        font-size: 2rem;
    }

    .counter button:hover {
        background-color: var(--color-bg-1);
    }

    svg {
        width: 25%;
        height: 25%;
    }

    path {
        vector-effect: non-scaling-stroke;
        stroke-width: 2px;
        stroke: #444;
    }

    .counter-viewport {
        width: 8em;
        height: 4em;
        overflow: hidden;
        text-align: center;
        position: relative;
    }

    .counter-viewport strong {
        position: absolute;
        display: flex;
        width: 100%;
        height: 100%;
        font-weight: 400;
        color: var(--color-theme-1);
        font-size: 4rem;
        align-items: center;
        justify-content: center;
    }

    .counter-digits {
        position: absolute;
        width: 100%;
        height: 100%;
    }

    .hidden {
        top: -100%;
        user-select: none;
    }
</style>
動作確認してみましょう。

「$:」について

コード内に「$:」とありすが、簡単に説明すると「count」の値が変わった時にしか動作しない処理です。
reactive
$: displayed_count.set(count);
最初なんやこれ??って思ったけど、しばらく使っていると慣れました。
慣れって怖い。

レイアウト用のファイルを作成してみる

routesディレクトリ内に「+layout.svelte」を追加してみましょう。
追加後に画面を確認してみると何も表示されなくなっているはずです。 +layout.svelteが存在しない場合は、デフォルトとして以下のコードになっているようです。
+layout.svelte
<slot />
今回使うことはないですが、ナビゲーションを追加してみましょう。 slotの部分に+page.svelteの内容が表示されると思ってもらえればOKです。
+layout.svelte
<nav>
    <a href="/">Home</a>
    <a href="#">About</a>
    <a href="#">Settings</a>
</nav>

<div class="app">
    <slot />
</div>
以下のように+layout.svelteにて、全画面で使うCSSファイルをインポートしてもOKです。
+layout.svelte
<script>
    import './styles.css';
</script>

ルーティングについて

ナビゲーションの「about」をクリックすると、ページ遷移するようにしてみます。 「http://localhost:[port]/about」でアクセスするイメージです。

フォルダとファイルを作成する

以下のように、routesディレクトリ内にaboutフォルダ、+page.svelteファイルを作成します。
+layout.svelte
<nav>
    <a href="/">Home</a>
    <a href="/about">About</a>
    <a href="#">Settings</a>
</nav>

<div class="app">
    <slot />
</div>
about/+page.svelte
<h1>About</h1>
ナビゲーションのAboutをクリックし、画面が遷移すればOKです。

まとめ

ReactやVueと比べると読みやすい気がしているので、
しばらく触ってみる予定です。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です