Svelte触ってみた　with React

# 謝罪とはじめに
担当日が酷い3日酔いのため掲載が遅れて本当にごめんなさい。これからは掲載の日は気合で記事書いていこうと思います。~~楽しい忘年会でした~~

こんにちは[MYJLab Advent Calendar](https://qiita.com/advent-calendar/2021/myjlab) 20日目　今日は個人的にいいことがありました阿左見です。

前回は[TDD](https://qiita.com/ren_180/items/de7fc7a41a0559225f71)について書きましたが今回はSvelteについて書いていこうと思います。
[2021年最も愛され、スポットを浴びたフレームワーク](https://insights.stackoverflow.com/survey/2021#section-most-loved-dreaded-and-wanted-web-frameworks)なsvelteは最近svelte summitなるものを開催しました。
なんか熱そうなので興味を持った次第です。

では、書いていこうと思います。


# Svelteとは何か
* **Write less code**
* **No virtual DOM**
* **Truly reactive**

[公式サイトによると](https://svelte.dev/)この3つが特徴として挙げられています
個人的に特筆すべきは**No virtual DOM**の部分かなと思っております。
また同サイトでは**traditional frameworks like React and Vue do**とか書かれており、仮想domがtraditionalはちょっと辛い。~~ついこの間まで仮想DOMしか勝たんとか言ってた筈では~~

## 仮想DOMは遅い!? 
またまた[公式サイト](https://svelte.dev/blog/virtual-dom-is-pure-overhead)ですが、**Virtual DOM is pure overhead**、副題にLet's retire the 'virtual DOM is fast' myth once and for all という記事があります。仮想DOM速いよ神話を終わりにしよう。だそうです。reactあたりにしっかり喧嘩売ってる気がします。

## 果たして本当に仮想DOMは遅いのか
結論から書きます。**仮想domは大抵、十分に速い**です最近のReactですとFiverが導入されたりとdomの更新をより小さな単位に分割できるようになりました。ReactやVueで用いられている差分検出のアルゴリズムは高速です。しかし、**仮想DOMは機能ではない**ことがこのsvelteの存在意義であるといえます。あくまでも手段である仮想DOMは宣言的で状態駆動型のUI開発を目的としています。仮想DOMをしようせずに同様のプログラミングモデルを実現できることが分かったのです。(=svelteの登場)

## 結局svelteってライブラリなの？フレームワークなの？
今までVueやReactとの比較をつらつらと書き連ねてきているので一体svelteってなんだよって思いますよね。
この疑問へは**フレームワークでもライブラリでもない**と答えるのが最善かと思います。**Svelteとはコンパイラです。**
単純に書く側の観点から見るとVueの単一ファイルコンポーネントに似ているのでフレームワークな気もしますが、厳密にはSvelteはコンパイラです。

ReactやVueはブラウザがコンパイル等の動作を行いますが、svelteは先にやっちゃうのでsvelteの方が早いよってことらしいです。

# コードで比較
説明するのが段々と辛くなってきました。日本語の記事少なすぎ問題はあると思います。
エンジニアならコード見た方が早いもんね！！！
と言うことでReactとSvelteで簡素なtodoアプリを作成していければと思います。SvelteもTypeScript対応してるのでTypeScript使っていきます。

今回のコードはhttps://github.com/renasami/advent にあげています。よければスターしてください。

## React編
普段通り``npx create-react-app --template typescript .``で作成していきます
create-react-appはアップデートされてるらしく``create-react-app@5.0.0``で作成できました。
http://localhost:3000 にアクセスするといつもの画面が現れるかとお思います。

今回のディレクトリ構成はこんな感じです。

```
src
|-App.tsx
|-Task.tsx
|-type.
```
はい。これだけです。非常にシンプルなtodoにしました。

```App.tsx
import React, { MutableRefObject, useRef, useState } from "react";
import Task from "./Task";
import { TaskType } from "./types";

function App() {
  const [tasks, setTasks] = useState<TaskType[]>([]);
  const ref = useRef(null);

  const addTask = (ref: MutableRefObject<any>) => {
    if (!ref.current.value) return;
    const newTask: TaskType = {
      id: tasks.length > 0 ? tasks[tasks.length -1].id + 1 : 0,
      text: ref.current.value,
      done: false,
    };
    setTasks([...tasks, newTask]);
    ref.current.value = "";
  };

  return (
    <>
      <h1>react todo</h1>
      <hr />
      <input type="text" placeholder="タスク入力" ref={ref} />
      <button onClick={() => addTask(ref)}>追加</button>
      <ul>
        {tasks.map((task,i) => {
          if (task.done) return null
          const props = {
            ...task,
            setTasks: setTasks,
            tasks: tasks,
          };
          return <Task {...props} key={i} />;
        })}
      </ul>
    </>
  );
}

export default App;
```

```Task.tsx
import React, { Dispatch, useRef } from 'react';
import { TaskType } from './types';

type Props = TaskType & {
    setTasks:Dispatch<TaskType[]>,
    tasks:TaskType[]
}

const Task:React.FC<Props> = (props: Props) => {
    const ref = useRef(null);
    const handleOnChange = () => {
        const newTasks = props.tasks.map((task,i) => {
            if (props.id !== i) return task
            return {
                id: task.id,
                text: task.text,
                done:!task.done
            }
        }) 
        props.setTasks(newTasks);
    }
    return ( 
        <li ref={ref} style={{display: "flex", margin:10}}>
            <button onClick={handleOnChange}>削除</button>
            <p style={{margin: 0}}>{props.text}</p>
        </li>
    )
}

```
App.tsにはタスクの追加の関数、Taskにはタスク自体を削除する関数を書いています。コードとして決して綺麗なコードではないですが、reactの状態管理としては基本的な構造じゃないでしょうか。
svelteの記事なので一旦reactはこんな感じでとどめます。

# svelte編
ディレクトリ構造はこんな感じです。

```
src
|-App.svelte
|-Task.svelte
|-types.ts
|-stores.ts
|-main.ts
```
store...? vueかな...?

```App.svelte
<script lang="ts">
	import Task from "./Task.svelte"
	import {taskList} from "./stores"
	let text = ""
	const addTask = () => {
		if (!text) return
		taskList.update((task) => [...task,{
			id:task.length,
			text:text,
			done:false
		}])
		text = ""
	}

</script>

<h1>svelte todo</h1>
<hr />
<div>
	<input bind:value={text}/>
	<button on:click={addTask}>追加</button>
	<ul>
	{#each $taskList as task }
		{#if !task.done}
			<Task task={task}/>
		{/if}
	{/each}
	</ul>
</div>
```

```Task.svelte
<script lang="ts">
    import {taskList} from "./stores"
    import type {TaskType} from "./types"

    export let task:TaskType
    const handleOnClick = () => {
        const newTask:TaskType[] = $taskList.map((t,i) => {
            if (task.id !== i) return t
            return {
                id: t.id,
                text: t.text,
                done:!t.done
            }
        })
        taskList.update(() => [...newTask])
    }
</script>

<li>
    <button on:click={handleOnClick}>削除</button>
    <p>{task.text}</p>
</li>

<style>
    li {
        display:flex;
        margin:10px;
    }

    p {
        margin:0;
        margin-left: 10px;
    }
</style>
```
```stores.ts
import { writable } from 'svelte/store'
import type { TaskType } from './types'

export const taskList = writable(<TaskType[]>[])
```
 
#### SvelteほぼVueじゃねえか
これが僕の正直な感想です。Vueの経験がある人は非常に書きやすいと思います。
AppとTaskで行っている処理は一緒です。
簡単に文法の説明だけしていこうと思います。

# バインディング
もう名前からvueな感じしますが、バインディングです。コードで言うとこの部分です

```
let text = ""

<input bind:value={text}/>
```
直感的にわかりやすいですが、inputの内容がtextに適宜代入されていきます。

# イベントハンドラ
この題で合ってるかわかりませんがクリックイベントなどを検知します。

```
<button on:click={handleOnClick}>削除</button>
```
例えばこの部分になります。これも直感的ですね。clickした時に関数が発火します。またchange等一般的なイベントにも対応しています。

# ロジック
vueでいうv-forであったりreactではmap関数を使用したりして描画する部分になります。

```
<ul>
	{#each $taskList as task }
		{#if !task.done}
			<Task task={task}/>
		{/if}
	{/each}
</ul>
```
この部分です。eachの部分でfor文の役割を果たしています。 ifはifですね。

# store
vuexやreduxのようにsvelteでは状態管理をstoreで行うことができます。

```stores.ts
import { writable } from 'svelte/store'
import type { TaskType } from './types'

export const taskList = writable(<TaskType[]>[])
```
writableとういうのがstoreの肝です。これもコードとしては直感的な部分になります。
これを呼び出し先の関数等からアップデートすることがきます。

非常に長くなりすぎるのでこれくらいにしておこうと思います。
vueと比べてみた方が良かったかなと感じています。
# まとめ
* Svelteはvueライクな構文で書くことが切る
* Svelteはそもそもライブラリ等ではなくコンパイラである
* 仮想domを用いていない

こんな感じではないでしょうか。個人的にvueの書き方は好きなので非常に書きやすかったです。
仮想domと比較した時のパフォーマンスやもう少し細かい記事は来年の頭にでも投稿しようかなと思います。

まだまだ未熟なのでコード見て気づいた点やsvelteの参考記事など英語でも構わないので有識者の方、よければコメントください。
# 参考文献
* [svelte 公式サイト](https://svelte.dev/)