Svelteの基本的な書き方

前回の記事でSvelteの概要について触れたので今回は具体的な書き方について書いていきます。

Svelteの構文

Svelteのコンポーネントhoge.svelteというファイル名になります。.svelteが拡張子です。

前回、ボタンをクリックするごとに数値がインクリメントするコードは以下の通りだと書きました。

<p>count: {counter}</p>
<button on:click={onClick}>hoge</button>

<script>
  let counter = 0
  const onClick = () => {
    counter++
  }
</script>

見ての通り、VueのSFCと同様にテンプレート部分とscript部分(とstyle部分)からなります。

作りとしてもVueに似ているので以降Vueに沿う形で説明していきます。

script

リアクティブな変数

Svelteではリアクティブな変数はletで定義します。ここではcounterがそれに当たります。

Vueで言うところのdataに該当すると考えてもらえれば大丈夫です(Vue3系だとrefになります)。

<script>タグの中で定義した変数はテンプレート内でも使用できます。

<p>count: {counter}</p>

ここで{counter}としているところに値が表示され、数値の更新が反映されます。

注意点として、Svelteでは変数への代入がリアクティブのトリガーになります。

なので、以下のようなコードでは更新されません。

<script>
  let members = []
  const addMember = () => {
    // pushでは更新されない
    members.push({
      name: '山田太郎',
      age: 20
    })
  }
</script>

<div>
  {#each members as member}
    <p>名前: {member.name}</p>
    <p>年齢: {member.age}</p>
  {/each}
</div>

トリガーとなるのは代入のみなので、以下のようにする必要があります。

<script>
  let members = []
  const addMember = () => {
    members = [...members, {
      name: '山田太郎',
      age: 20
    }]
  }
</script>

<div>
  {#each members as member}
    <p>名前: {member.name}</p>
    <p>年齢: {member.age}</p>
  {/each}
</div>

他の変数に依存する変数

Vueのcomputedに相当するような、他の変数の値によって変化する値は以下のように書きます。

<script>
  let num = 1
  $: doubled = num * 2
</script>

$:を付けることで依存する他の値の変更を検知し、更新されるようになります。

単一の変数だけでなく、ブロック化することもできます。

<script>
  let num = 1
  $: {
    console.log(num * 2)
  }
</script>

コンポーネントに値を渡す

Vueのpropsのように子コンポーネントに値を渡したい場合は以下のようにします。

<!-- 親コンポーネント -->
<script>
  let hoge = 'hogehoge'
</script>

<div>
  <Child hoge={hoge} />
  <!-- 親コンポーネントの変数名と子コンポーネントの変数名が同じ場合は以下でもOK -->
  <Child {hoge} />
<div>
<!-- 子コンポーネント -->
<script>
  // 子コンポーネントでexportする
  export let hoge;
</script>

<div>
  <span>{hoge}</span>
</div>

上記の通り、子コンポーネントexportすることでコンポーネントのプロパティとすることができます。

「親から子に渡すんだから親コンポーネントでexportするんじゃないの?」と違和感がありますが、ここは慣れかと思います。

テンプレート

続いてテンプレート部分の構文も見ていきます。

ループ

Vueでは配列をループするときにはv-forを使いました。

Svelteでは先程出てきたコードのように{#each}を使います。

<div>
  {#each members as member}
    <p>名前: {member.name}</p>
    <p>年齢: {member.age}</p>
  {/each}
</div>

配列のインデックスを第2引数に取ることもできます。

<div>
  {#each members as member, index}
    <p>会員番号: {index}</p>
    <p>名前: {member.name}</p>
    <p>年齢: {member.age}</p>
  {/each}
</div>

if文

条件分岐するときは{#if}を使います。

<div>
  {#if num > 100}
    <span>{num}は100より大きい</span>
  {/if}
</div>

条件を増やすときは{:else}{:else if}を使います。

<div>
  {#if temperature > 25}
    <span>暑い</span>
  {:else if temperature < 15}
    <span>寒い</span>
  {:else}
    <span>快適</span>
  {/if}
</div>

await

{#await}を使うとPromiseが解決されるまでの状態によってレンダリングする内容を変えることができます。

<div>
  {#await promise}
    <!-- promiseが解決される前 -->
    <span>Loading...</span>
  {:then value}
    <!-- 完了したとき -->
    <span>Success! value is {value}</span>
  {:catch error}
    <!-- エラーが発生したとき -->
    <span>Failed! error: {error.message}</span>
  {/await}
</div>

{:catch}が不要な場合は省略できます。また、解決する前に何もレンダリングする必要が無い場合は以下のように書けます。

<div>
  {#await promise then value}
    <span>Success! value is {value}</span>
  {/await}
</div>

key

{#key}を使うと値の変更を検知して再レンダリングします。トランジションと併せて使うことが多いです。

<div>
  {#key value}
    <span in:fly={{ y: -20 }}>hogehoge</span>
  {/key}
</div>

イベントハンドラ

Vueではclick等のイベントハンドラv-on:clickまたは@clickと書きます。

Svelteではon:clickと書きます。

これはclickやhover等のDOMイベント以外にもカスタムイベントも扱えます。

ではVueの$emitのように子コンポーネントから親コンポーネントにイベントを伝播するにはどうすれば良いかというと以下の通りです。

<!-- 親コンポーネント -->
<script>
  const hoge = (event) => {
    // 子から渡した値はevent.detailの中に入っている
    console.log(event.detail.value)
  }
</script>

<div>
  <Child on:hoge={hoge} />
</div>
<script>
  import { createEventDispatcher } from 'svelte'
  const dispatch = createEventDispatcher()
  const clickDispatcher = () => {
    // 第1引数にイベント名、第2引数にdetailを取る
    dispatch('hoge', {
      value: 'hogehoge'
    })
  }
</script>

<div>
  <button on:click={clickDispatcher}></button>
</div>

createEventDispatcherを使うことでカスタムイベントを作成できます。値を渡したい場合は上記のように第2引数に入れればOK。

まとめ

以上、Svelteの基本的な書き方についてまとめました。

他にもStoreやbind等色々ありますが、とりあえずこの記事を見ればある程度は書けるようになるのではないかと思います。

ざっくりした印象ですがやはりVueに似ていると思います。なのでVueを使ったことがある人ならそこまで抵抗なく始められるはず。

慣れると結構サクサク書けると思うのでぜひやってみてください。