はじめに
初カキコ…ども…
RUNTEQ Advent Calendar 2022 5日目の記事となります。
最近ちらほらと噂で耳にするSvelte・・・
The State of JS 2021においても、ReactやVueを抑えてSvelteの方が満足度が高いとの結果が出ています。
「ReactやVueを必死にキャッチアップしたけど、また流行り変わっちゃうかもしれないの〜!?」と、
ゲンナリする気持ちが少し出そうですね。
日本でも流行るかどうか現状ではわかりませんが、
いざ実務で使うってなった時に「あ、Svelteっすね、楽勝です」と大見得を切れるように、
先に触れて楽しく遊んでおくのがこの世界で生き残るコツかもしれない。
そんな思いで本記事を作成しています。
そもそもSvelteって?
公式によると以下の3つが特徴のようです。
- Write less code(コード量を減らす)
- No virtual DOM(仮想DOMを使用しない)
- Truly reactive(真にリアクティブ)
OKです、OKです、なんとなく凄そうですね。
もう少し詳しく見ていくために、
いま流行りのChatGPT先生に聞いてみましょう。
いや〜〜〜、、、
予想以上にそれっぽい文章が出てきました。コレすごいですね。
とりあえず、キモとなる部分は、 Svelteはコンパイラである という点のようです。
参考:Svelteの根本部分を簡単に理解する
この記事でやること
さきほど紹介した特徴の一つである、Write less code(コード量を減らす) について、
VueのTodoアプリをSvelteに作りかえていくことで比較してみようと思います。
題材となるVueのアプリはCodeSandBoxで探してきた以下のアプリを使わせてもらいます。
Vue3系ですが、Composition APIは使っていません。
Vue 3 Todo App
Svelteで作り変えた完成形はこちら。
Svelte_TODO
CodeSandBoxにて公開しているので、気軽にいじってみてください。
キャッチアップ方法
公式のTutorialを6章くらいまでさらっと行いました。(所要時間2h)
REPL上で学べてとんでもなく優秀なチュートリアルでした!楽しい!
簡単なTodoアプリなので、以降はチュートリアルを逆引しつつ作成しました。
さっそく作ってみた
まずは完成形から。style部分については共通なので省略してます。
Vueと同じ挙動になるように忠実に作りかえたので、追加の機能などは入れていません。
Vueのコード
<script>
class Todo {
constructor(title, isCompleted) {
this.isCompleted = isCompleted;
this.title = title;
}
}
export default {
name: "App",
data() {
return {
todos: [],
todo: "",
};
},
methods: {
addToList: function () {
let newTodo = new Todo(this.todo, false);
this.todos.push(newTodo);
this.todo = "";
},
removeFromList: function (index) {
this.todos.splice(index, 1);
},
},
components: {},
};
</script>
<template>
<div class="container">
<div class="title">
<h1>Todos</h1>
</div>
<div class="todo-list-container">
<ul v-if="todos.length > 0">
<li v-for="(todo, index) in todos" :key="index">
<p>{{ todo.title }}</p>
<input type="checkbox" v-model="todo.isCompleted" />
<button @click.prevent="removeFromList(index)">Delete</button>
</li>
</ul>
</div>
<div class="form-container">
<form>
<label for="todo">New Todo</label>
<input type="text" v-model="todo" />
<button type="submit" @click.prevent="addToList">Submit</button>
</form>
</div>
</div>
</template>
Svelteのコード
<script>
let todo = { title: "", isCompleted: false };
let todos = [];
function addToList() {
// todoListにtodoを追加する
todos = [
...todos,
{
title: todo.title,
isCompleted: false
}
];
// todoを初期化する
todo = { title: "", isCompleted: false };
}
function removeFromList(index) {
// todoListからtodoを削除する
todos = todos.filter((_, i) => i !== index);
}
</script>
<main>
<div class="container">
<div class="title">
<h1>Todos</h1>
</div>
<div class="todo-list-container">
{#if todos.length > 0}
<ul>
{#each todos as { title, isCompleted }, index}
<li>
<p>{index + 1 }:{title}</p>
<input type="checkbox" bind:checked={isCompleted} />
<button on:click={() => removeFromList(index)}>Delete</button>
</li>
{/each}
</ul>
{/if}
</div>
<div class="form-container">
<form>
<label for="todo">New ToDo</label>
<input type="title" bind:value={todo.title} />
<button type="submit" on:click|preventDefault={addToList}>Submit</button>
</form>
</div>
</div>
</main>
ポイント解説
<script>
let todo = { title: "", isCompleted: false };
</script>
<input type="text" bind:value={todo.title} />
<input type="checkbox" bind:checked={todo.isCompleted} />
<p>{todo.title}</p>
<p>{todo.isCompleted}</p>
これだけで以下の挙動が実現できます。
bind:value
などのイベント記述の部分はVueに似ていますが、変数定義などはReactっぽい感じでしょうか。
シンプルですね。
<script>
let todo = { title: "", isCompleted: false };
let todos = [];
function addToList() {
// todoListにtodoを追加する
todos = [...todos,
{
title: todo.title,
isCompleted: false
}
];
// todoを初期化する
todo = { title: "", isCompleted: false };
}
</script>
<form>
<input type="text" bind:value={todo.title} />
<button type="submit" on:click|preventDefault={addToList}>Submit</button>
</form>
<ul>
{#each todos as { title, isCompleted }}
<li>
<p>{title}</p>
<input type="checkbox" bind:checked={isCompleted} />
</li>
{/each}
</ul>
まず、addToList
関数を呼び出す際に、form
を利用しているので、イベント修飾子をつけています。
この辺の書き心地はvueと変わりないですね。
参考:Vue.jsイベント修飾子.stopと.preventの使いどころ
参考:チュートリアル@event-modifiers
// svelteのコード
<button type="submit" on:click|preventDefault={addToList}>Submit</button>
// vueのコード
<button type="submit" @click.prevent="addToList">Submit</button>
addToList
関数は、ES6 のスプレッド構文を使用しています。
Svelteのリアクティビティは代入によってトリガーされます。
参考:チュートリアル@updating-arrays-and-objects
// svelteのコード
let todo = { title: "", isCompleted: false };
let todos = [];
function addToList() {
// todoListにtodoを追加する
todos = [...todos,
{
title: todo.title,
isCompleted: false
}
];
// todoを初期化する
todo = { title: "", isCompleted: false };
}
// vueのコード
data() {
return {
todos: [],
todo: "",
};
},
methods: {
addToList: function () {
let newTodo = new Todo(this.todo, false);
this.todos.push(newTodo);
this.todo = "";
}
}
html部分は、each
ブロックで配列をまわしてます。
分割代入の仕方なども特段難しいところはありません。
Vueと比較したときに、Svelteの方が可読性は高い気がしました。
参考:チュートリアル@each-blocks
// svelteのコード
{#each todos as { title, isCompleted }}
<li>
<p>{title}</p>
<input type="checkbox" bind:checked={isCompleted} />
</li>
{/each}
// vueのコード
<li v-for="(todo, index) in todos" :key="index">
<p>{{ todo.title }}</p>
<input type="checkbox" v-model="todo.isCompleted" />
</li>
<script>
// ・・・(中略)・・・
function removeFromList(index) {
// todoListからtodoを削除する
todos = todos.filter((_, i) => i !== index);
}
</script>
// ・・・(中略)・・・
{#each todos as { title, isCompleted }, i}
<li>
<p>{title}</p>
<input type="checkbox" bind:checked={isCompleted} />
<button on:click={() => removeFromList(i)}>Delete</button>
</li>
{/each}
まずはremoveFromList
関数の呼び出し。
この辺りはvueとほとんど同じです。
// svelteのコード
<button on:click={() => removeFromList(i)}>Delete</button>
// vueのコード
<button @click="() => removeFromList(index)">Delete</button>
配列から削除する関数は以下です。
indexを渡してfilter
で配列から取り除くだけです。
// svelteのコード
function removeFromList(index) {
todos = todos.filter((_, i) => i !== index);
}
// vueのコード
removeFromList: function (index) {
this.todos.splice(index, 1);
}
ちなみにですが、元のvueのコードはsplice
を使って配列から取り除いており、svelteで行おうとすると以下のようになります。
svelte のリアクティビティは代入によってトリガーされ、配列やオブジェクトを変更するようなメソッドでは、その更新がトリガーされないためです。
spliceは破壊的に取り除くけど、 filterなら非破壊で新しい配列を作ってくれます。
function removeFromList(index) {
todos.splice(index, 1);
todos = todos;
}
// もしくは以下
function removeFromList(index) {
todos = [...todos.slice(0, index), ...todos.slice(index + 1)];
}
// 以下では挙動がおかしくなる
function removeFromList(index) {
todos = todos.splice(index, 1);
}
ざっくりと所感
今回は、簡単なTodoアプリだったので、コンポーネントの分割など一切行っていませんが、
基本的な書き心地はVueにかなり似ていて理解しやすかったです。
チュートリアルの分かりやすさと相待ってVueを学んでいた方はすんなりキャッチアップできると思います。
今回使っていない機能が多々ありますが、Svelteならではのオモシロ部分は以下の記事が分かりやすかったです。
参考:Vueユーザーが感じたSvelteのおもしろい機能を紹介する
とはいえ、JSをしっかりと学べばいきなりSvelteから入っても大丈夫だと思うので、
駆け出しエンジニアからチャレンジしてみるのもいいかもしれません。
今回の目的であるWrite less code(コード量を減らす) に関して言えば、
Vue3のCompotisionAPI
を利用するとかなりスッキリするので、
Svelteが劇的にすごい!とは思いませんでした。(今回のVueアプリでは使っていませんでしたが・・・)
もう少し大きいアプリで試してみると、メリット・デメリットがわかってくると思うので、またいずれチャレンジしてみたいです。