はじめに
こんにちは、株式会社iimonでフロントエンドエンジニアをしている「みよちゃん」です。
本記事はアドベントカレンダー10日目の記事になります!
自分が参画しているプロダクトでは普段FWを使っていないのですが、せっかくのアドベントカレンダーなので普段やらないことをやりたい!ということで、少し(?)前から流行っているSvelteを触ってみることにしました!
後半は実際にSvelteを動かしながら、その記述方法や特徴にも触れているため、ぜひ一緒に動かしてみてください!
Svelteの特徴
Svelteとは
Svelteは、webアプリケーションを構築するためのツールです。他のUIFW同様「マークアップ」「スタイル」「振る舞い」を組み合わせたコンポーネントアプリを宣言的に構築することができます。これらのコンポーネントは小さくて効率的な JavaScript モジュールに コンパイル されるため、従来の UI フレームワークには付き物だったオーバーヘッドがありません。
Svelteの特徴
高速
SvelteはReactやVueとは異なりFWではなくコンパイラです。
そのために以下のような特徴を持ちます。
- ビルド時に最適化されたコードを生成する
- ランタイムオーバーヘッドがほぼない
- リアクティブシステムも事前生成される
結果としてSvelteはとても高速です。
記述がシンプル
Svelteは公式でも「ちょっと素敵なものを加えただけのHTML」と表現されているように、HMLT・CSS・JaveScriptの理解があればなんとなく理解できます。
仮想DOMを使用しない
Svelteは、ReactやVueのように仮想DOMを使用せず、直接コードを操作します。
ReactやVueなどのFWとの比較
現在フロントエンド界隈を牛耳っている「React」や「Vue」との比較が気になる方もおられると思うので、トレンドやパフォーマンスを比較してみましょう。
トレンド
State of JavaScriptの調査によると利用数では、「React」「Vue」「Angular」に次ぐ4位となっています。。。徐々に数字を伸ばしていますが、特徴的に大規模なプロダクトには向かないのかもしれません。。
しかし!!落胆することはありません!!
次にユーザーの興味の推移を見てみましょう!!! Svelteは登場して以来、最も使用したいと考えているフレームワークで常に上位にランクしています!
Svelteの弱点・デメリット
- エコシステムが小さい
- SvelteはReactと比較すると登場してからの日が浅いため、外部ツールなどの環境が十分とはいえません。ここは今後の発展に期待かな〜と思います。
- 独自の記法
- 普段ReactでJSXを使用している開発者からすると、馴染みのない記法があり、慣れるまでには時間がかかる可能性があります。
実際に動かしてみる
それでは実際にSvelteのプロジェクトを作成して動かしてみましょう!!
アプリの作成
公式チュートリアルでは
npm create svelte@latest myapp
とするように書かれていますが、現在は
npx sv create myapp
に置き換わっているようです。
コマンドを実行するとどのtemplateを使用するか聞かれます。
なるほどわからん。。。
調べてみると
- SvelteKit minimal
- 最低限の構成、パッケージなどは自身でインストールする
- SvelteKit demo
- 学習やデモように最適化されている。Svelteの基本的な機能を学ぶサンプルコード付き
- SvelteKit minimal
- 再利用可能なライブラリやコンポーネントを作成するためのテンプレート。Svelteを使ってパッケージ化可能なモジュールを作成するのに適している。 とのこと。今回はせっかくなので最低限の構成にします!
次にTypeScriptを使用するかを聞かれます。
筆者はiimonで初めてTypeScriptを触ってからというものの、型がないと怖くて実装ができなくなってしまったので使用します!
何やらパッケージも追加できるみたいですが、今回はお試しなので何も追加しません!!
※SvelteはNodeのv18.13以前はサポートしていないようなので試される方は注意してください
以下のような画面が出たら、プロジェクト作成完了です!
起動確認
それでは、myappディレクトリに移動して実際に起動してみましょう!
cd myapp npm run dev
ブラウザを開き、http://localhost:5173/に移動します。
src/routes/にある+page.svelteの内容が表示されていれば準備完了です!!
色々試してみる
実際にSvelteを動かしながら、特徴を見ていきたいと思います!
画面表示とスタイル
+page.svelteに以下のように記述します。
<script> import OtherComponent from "./OtherComponent.svelte"; let name = 'iimon' </script> <style> p{ color:red; font-size: 2rem; } </style> <p>Hello, {name}</p> <OtherComponent/>
styleの挙動も確認したいので、+page.svelte
と同じディレクトリ内にOtherComponentというファイル(名前はなんでも良いです)を作成し、それぞれ以下のように記述します
<p>This is other component .</p>
実際ブラウザにはこのように表示されます
まず+page.svelteファイルに注目してください。Svelteではscripタグ内に振る舞いを、styleタグ内に装飾を記述します。
先ほども記述した「ちょっと素敵なものを加えただけのHTML」という公式の表現もしっくりきますね!
さらに注目していただきたいのは、styleで指定した装飾はコンポーネント内にのみ影響するということです。
import してきたOtherComponentには影響していません。
カウンターを作ってみる
次にReactのチュートリアルでも時々目にするカウンターを作ってみたいと思います!
+ボタンをクリックするとカウントが+1され、-ボタンをクリックするとカウントが1されます。
さらにcountが変換するたびに2倍を計算する機能もつけましょう!
完成像はこんな感じです↓↓
+page.svelte
<script> let count = 0; const countUp = () =>{ count += 1; } const countDown = () => { count -= 1; } $: double = count * 2 </script> <p>現在のカウントは{count}回です</p> <p>カウントを2倍すると{double}です</p> <button on:click={countUp}>+</button> <button on:click={countDown}>-</button>
皆さんは今「え?これだけ?」と声に出してしまったことでしょう。
自分も先日電車の中でサッカーの試合を見ていたんですが、セルティック所属の「前田大自然」選手のスーパーゴールを見た時、そこそこの声量で「嘘でしょ!?」と発してしまいました。
余談は置いておいて、Reactと比較してもとてもシンプルな記述ですよね!!
ちなみに同じ処理をReactで記述するとこのようになります。
import React, { useState, useEffect } from 'react'; function Counter() { const [count, setCount] = useState(0); const [double, setDouble] = useState(0); const countUp = () => { setCount(prevCount => prevCount + 1); }; const countDown = () => { setCount(prevCount => prevCount - 1); }; useEffect(() => { setDouble(count * 2); }, [count]); return ( <div> <p>現在のカウントは<strong>{count}</strong>回です</p> <p>カウントを2倍すると{double}です</p> <button onClick={countUp}>+</button> <button onClick={countDown}>-</button> </div> ); }
今回のコードはSvelteの以下の二つの特徴がシンプルたらしめているかと思います。
状態管理
Reactでは状態管理をuseStateで行いますが、Svelteでは複雑な管理を必要としません。ただ変数を更新するだけです。
リアクティブシステム
Svelteでは$:
に続いて記載されている式内の変数の依存関係を把握します。そして、依存する変数の値が変更された場合に実行されるため、シンプルにUIの更新を管理することができます。
Todoアプリを作ってみる
最後にシンプルなTodoアプリを作ってみましょう!
完成イメージはこんな感じです!!
長くなるのでStyleの記述は省略させていただきます。。
+page.svelte
<script lang="ts"> import type { TodosType } from "$lib"; let todos: TodosType = []; let newTodo: string = ""; const addTodo = () => { if (newTodo.trim()) { todos = [...todos, { id: Date.now(), text: newTodo, completed: false }]; newTodo = ""; } }; const toggleTodo = (id: number) => { todos = todos.map((todo) => todo.id === id ? { ...todo, completed: !todo.completed } : todo ); }; const deleteTodo = (id: number) => { todos = todos.filter((todo) => todo.id !== id); }; </script> <div class="container"> <h1>ToDo</h1> <div class="todo-input"> <input type="text" placeholder="Add a new task" bind:value={newTodo} /> <button on:click={addTodo}>Add</button> </div> <ul> {#each todos as todo (todo.id)} <li class="todo-item {todo.completed ? 'completed' : ''}"> <span on:click={() => toggleTodo(todo.id)}> {todo.text} </span> <button on:click={() => deleteTodo(todo.id)}>Delete</button> </li> {/each} </ul> </div>
双方向バインディング
ここで注目していただきたいのはbind:value={newTodo}
の部分です。
Svelteのbind:valueは、フォーム入力要素の値をSvelteの変数に双方向でbindすることのできる仕組みです。
双方向バインディングとは、フォームの値が変わるとSvelteの変数も更新され、逆に変数が更新されるとフォームの値も自動的に変更されることを言います。この金曜により、変数とUIが常に同期した状態を保つことができます。
ReactではvalueとonChangeをペアで記述する必要がありますが、Svelteではbind:valueを使用するだけで良いので、より簡潔に記述できます。
また、Svelteでは専用の{#each}構文があり、(tod.id)でキーの指定をしています。キーの指定は省略でますが、キーを指定した方が効率的にレンダリングされるようです。
おわりに
今回は登場から今に至るまで技術者の高い興味関心を惹きつけているSvelteを実際に触ってみながら、その基本的な動きを見ていきました。基礎的な部分にしか触れてきませんでしたが、Svelteはその特徴から、「シンプルで軽量なアプリ開発を開発した」「学習コストをかけたくない」「DOM操作やUI更新のパフォーマンスを重視したい」と言った希望のある開発者からは大変重宝されるのだろうなと感じました。今後、独自のストア機能など今回触れることができなかった内容にもチャレンジしていきたいと思います。
明日のアドベントカレンダー担当は、iimon期待のニューウェーブ「ばんり」さんです!どんな記事を投稿するのか今から楽しみです!!
最後になりますが、現在弊社ではエンジニアを募集しています!
この記事を読んで少しでも興味を持ってくださった方は、ぜひカジュアル面談でお話ししましょう!
最後まで読んでいただきありがとうございました!!!