Vue.jsが書ければSvelteもすんなり書ける？書き方を比較してみた

せっかくSvelteのアドベントカレンダーに参加させていただいているので、Svelteを利用する方が少しでも増えてもらえそうな記事に挑戦してみようと思います。
今回の記事ではVue.jsの書き方とSvelteの書き方を比較してみました。普段Vue.jsを使っている方がSvelteにも興味を持っていただけたら嬉しいです🙌

# コンポーネントの基本構造
Vue.jsもSvelteもコンポーネントの基本構造はほとんど同じです。Vue.jsを書いてきた方は違和感なくSvelteで書かれたコンポーネントの実装を読むことができます。
``` html: Vue.js
<script setup lang="ts">
// imports
// logic
</script>

<template>HTML goes here</template>

<style></style>
```

``` html: Svelte
<script lang="ts">
  // imports
  // logic
</script>

<!--HTML goes here-->

<style></style>
```

# 要素プロパティへのバインディング
フォームバインディングの中でも一番シンプルなテキストを例に挙げてみます。
Vue.jsはリアクティブな参照として機能させるにはrefメソッドを使用する必要があります。またバインディングにはv-modelが提供されています。
一方でSvelteは特有のメソッドを呼び出す必要がなく、変数の定義だけで済みます。バインディングにはbindが’提供されています。
多少実装の違いがありますが、HTMLを拡張するような書き方はどちらもイメージが近いですね。
``` html: Vue.js
<script setup lang="ts">
    import { ref, type Ref } from 'vue';
    const text: Ref<string> = ref('Hello');
</script>

<template>
  <section>
    <h2>Text Input</h2>
    <input v-model="text" />
    <p>{{ text }}</p>
  </section>
</template>
```

``` html: Svelte
<script lang="ts">
	let text: string = 'Hello';
</script>

<main>
  <section>
    <h2>Text Input</h2>
    <input bind:value={text}>
    <p>{text}</p>
  </section>
</main>
```

# イベントハンドリング
ボタンクリックのイベントハンドリングを例にしてみましたが、ほぼほぼ同じですね。
Vue.jsは＠clickでボタン要素をメソッドハンドラーにすることができます。
Svelteはon:clickでボタン要素をメソッドハンドラーにすることができます。

またどちらもイベント修飾子をつけることができます。繋げることで修飾子の連鎖も可能。
Vue.jsは `<a @click.stop.prevent="doThat"></a>`
Svelteは`<a on:click|stop|prevent="doThat"></a>`

``` html: Vue.js
<script setup lang="ts">
  const handleClick = () => {
    alert('I was clicked!');
  };
</script>

<template>
  <button @click="handleClick" class="button">
    Click me!
  </button>
</template>
```

``` html: Svelte
<script lang="ts">
  const handleClick = () => {
    alert('I was clicked!')
  }
</script>

<main>
  <button on:click={handleClick} class="button">
    Click me!
  </button>
</main>
```

# ロジック
コンポーネント内のロジック実装も比較しています。
Vue.jsはv-if,v-forが提供されています。
Svelteは{}に囲んだ#if, #eachが提供されています。
ロジック周りの実装も違和感なく使いこなせるのではないでしょうか？

## if/else
``` html: Vue.js
<script setup lang="ts">
  import { ref } from 'vue';
  const isVisible = ref(true);
</script>

<template>
  <p v-if="isVisible">I'm right here!</p>
  <p v-else>I'm invisible!</p>
</template>
```

``` html: Svelte
<script lang="ts">
  let isVisible:boolean = true;
</script>

{#if isVisible}
  <p>I'm right here!</p>
{:else}
  <p>I'm invisible!</p>
{/if}
```
## each
``` html: Vue.js
<script setup lang="ts">
import { ref } from 'vue';
const jobs = ref([
  { id: 1, title: 'Frontend Developer' },
  { id: 2, title: 'Backend Developer' },
  { id: 3, title: 'Fullstack Developer' },
]);
</script>

<template>
  <ul>
    <li v-for="job in jobs" :key="job.id">
      {{ job.title }}
    </li>
  </ul>
</template>
```

``` html: Vue.js
<script lang="ts">
  let jobs = [
    {id: 1, title: "Frontend Developer"},
    {id: 2, title: "Backend Developer"},
    {id: 3, title: "Fullstack Developer"}
  ];
</script>

<ul>
  {#each jobs as {id, title}, i}
    <li id={id}>
      {title}
    </li>
  {/each}
</ul>
```


# ライフサイクル
ライフサイクル関数をいくつか比較しています。

コンポーネントがDOMにマウントされた後に実行したい処理は、
Vue.jsなら`onMounted`、Svelteなら`onMount`

コンポーネントが更新された直後に実行したい処理は、
Vue.jsなら`onUpdated`、Svelteなら`afterUpdate`

コンポーネントがアンマウントされる直前に実行したい処理は、
Vue.jsなら`onUnmounted`、Svelteなら`onDestroy`

``` html: Vue.js
<script setup>
  import { onMounted, onUpdated, onUnmounted } from 'vue';
  onMounted(() => {
    console.log('the component is now mounted.');
  });
  onUpdated(() => {
    console.log('the component is updated.');
  });
  onUnmounted(() => {
    console.log('the component is destroyed.');
  });
</script>
```

``` html: Svelte
<script lang="ts">
  import { onMount, afterUpdate, onDestroy } from 'svelte';
  onMount(() => {
    console.log("the component is now mounted.")
  });
  afterUpdate(() => {
    console.log("the component is updated.")
  });
  onDestroy(() => {
    console.log("the component is destroyed.")
  });
</script>
```

# Reactive Statement
Vue.jsではリアクティブに扱う値を `computed` や `watch` で宣言してあげる必要があります。一方でSvelteでは`$:` を使うことで宣言したものやロジックがリアクティブに作用します。

ここまで比較してきた中でここが一番ギャップがあるかもしれません。逆に言うとリアクティブに関する実装の勘所が掴めればもうSvelteもVue.jsと同じ要領で書けるようになると思います！


``` html: Vue.js
<script setup lang="ts">
import { ref, computed, watch } from 'Vue'
let count = ref(0)

let doubled = computed(() => count.value * 2)
watch(count, (newValue) => {
  if(newValue >= 10) {
    alert('count is dangerously high!');
    count.value = 9
  }
})

const handleClick = () => count.value += 1
</script>

<template>
  <button @click="handleClick">
    Clicked {{count}} {{count === 1 ? 'time' : 'times'}}
  </button>
  <div>
    Doubled: {{doubled}}
  </div>
</template>
```

``` html: Svelte
<script lang="ts">
  let count = 0;
  
  $: doubled = count * 2
  $: if (count >= 10) {
    alert('count is dangerously high!');
    count = 9;
  }
  
  const handleClick = () => count += 1;
</script>

<button on:click={handleClick}>
  Clicked {count} {count === 1 ? 'time' : 'times'}
</button>
<div>
  Doubled: {doubled}
</div>
```


# まとめ
基本的な書き方についてまとめてみました。
Vue.jsが書ける方から見て、Svelteも書けそうだな！と思えたでしょうか？

Svelteはドキュメント、チュートリアルが充実していますので、興味のある方はぜひチェックしてみてください🙋‍♂️
[ドキュメント](https://svelte.jp/docs)
[チュートリアル](https://svelte.jp/tutorial/basics)