フューチャー技術ブログ

Svelteに入門した

フロントエンド連載の6記事目です。

今年のゴールデンウィーク(STAY HOME週間)に最近話題のSvelteに入門したので紹介を書きます。

Svelteとはなんですか?

svelteロゴ

公式のサイトはこちらです。有志の方々日本語翻訳のサイトを作ってくれています。たいへんありがとうございます!

Svelteは主にブラウザ上で動作するユーザーインタフェースを作るフレームワークで、ReactVue.jsの対抗馬的な存在です。

特徴とReactやVue.jsなどほかとの違い

公式サイトでも、コーディングする際のコード量が少ないという特徴があげられています。

詳しくはこちらのブログに書かれています。コードが多ければ作業時間とバグが増えてしまうため、コードが減らすことはこれらの問題を減らすことができるというようなことが書いてありました。またブログには具体的なコードで量の差について書いていますのでぜひ見てみてください。

Svelteは主にコンパイラであり、ReactやVueと比べるとSvelteはランタイムをほとんど提供しません。これによって構築するアプリケーションの特性によってはリソースサイズを最小に抑えられる可能性があります。

さらに、Svelteのコンパイラは、Lintのようなコードチェックを行います。基本的なところだと、未使用変数や未定義変数の使用を警告してくれたりしますが、おもしろいところだと未使用のCSSセレクタを検出して警告してくれたり、アクセシビリティ(A11Y)的に問題のあるHTMLを検出して警告してくれます。

アクセシビリティのチェックは、ReactやVueでもESLintプラグインなどを使用すればチェックできますが、Svelteはこれを標準で行うというところが先進的で良い取り組みだと思います。

そのほか、公式サイトであげられている特徴として、仮想DOMなし(No virtual DOM)真のリアクティブ(Truly reactive)があります。

Svelteの開発元

SvelteはRich Harrisさんを中心としたチームによって作成されています。Rich HarrisさんはRollupの作者としても有名です。あとホーム・アローンという映画に出てくる人らしいです(嘘です。冗談です)。
Svelteは、ReactやAngularのように企業がバックにいるわけではありませんが、Rich Harrisさんが現在属しているNew York TimesではSvelteを積極的に使用しているようです。

Svelteの近況

毎年年末ぐらいに発表される The State of JavaScript の2019年フレームワークランキングでSvelteが初登場し上位に食い込みました。そして2020年には1位となりました。世界的にはかなり注目と期待が集まっているということだと思います。

日本の話だと、2021年4月24日に発売されたWEB+DB PRESS Vol.122 でSvelteの記事が載ってました。日本でも注目が集まっているということでしょうか?

Svelteに入門する

Svelteに入門するために開発環境を整える必要はありません。Svelteは公式のREPLというブラウザ上で簡単に試すことができるしくみがあります。

そして、Svelteに入門するためのチュートリアルも説明とともにREPL上で試しながら学べるように作られています。

入門するために環境を用意するのは地味に面倒くさいので、時間とブラウザがあれば簡単入門できるこのしくみはとても便利です。

もしローカルで動かして試したくなった場合は、REPLページの上の方にダウンロードボタンがあるので、そこからダウンロードしたzipファイルを解凍してそのディレクトリでnpm i && npm run devするだけで同じものをローカルで試すことができます。

さらに、REPLで動作するサンプルコードが次のリンクにたくさん用意されています。

自分が触り始めたい状態のサンプルを見つけたらREPL→を押してダウンロードボタンからダウンロードするだけで簡単に同じ状態をローカルに構築できます。とても簡単です!

Svelteの基本的な構文

いくつかのSvelte構文を紹介しますが、この記事では詳細を書かないので少しでも興味が湧いたらチュートリアルを見てもらうとよいと思います。

Svelteでは.svelteという拡張子のファイルを使用します。これらをSvelteでコンパイルすることによってWebアプリケーションを構築していくことになります。.svelteファイルはVueの単一ファイルコンポーネントと同じで、HTML、JavaScript、CSSを記述して一つのUIコンポーネントを作成します。

たとえば次のように書きます。

<script>
let name = 'world';
</script>

<h1>Hello {name}!</h1>

<style>
h1 {
color: blue;
}
</style>

動作させると次のように表示されます(REPLにソースコードを貼り付けるだけで簡単に試せます!)

REPLの表示

それではいくつかの構文について紹介していきます。

バインディング・テキスト挿入

中括弧{}を使用してテンプレートにテキストを埋め込むことができます。
次は簡単な例です。

<script>
let name = 'world';
</script>

<h1>Hello {name}!</h1>

Svelteは<script>に書かれたJavaScriptのトップレベルで宣言された変数や関数をテンプレートで使用できます。上記の例ではname変数を宣言し、テンプレートで{name}と書くことでこの部分にname変数の値がテキストとして表示されます。

上記のコードは実際には次のHTMLが表示されます。

<h1>Hello world!</h1>

中括弧{}は属性にも記述できます。

<script>
let src = 'tutorial/image.gif';
let name = 'Rick Astley';
</script>

<img src="{src}" alt="{name} dances.">

このソースコードは<img>src属性にsrc変数の値が与えられ、alt属性にname + " dances."が与えられます。

src="{src}"部分はさらに省略できます。属性に与えるデータが一つの変数(および式)の場合は、引用符を省略できます。つまり、src={src}と書くことができます。さらに、属性名と与える変数が同じ場合{src}と書くだけで同じことができます。

<script>
let src = 'tutorial/image.gif';
let name = 'Rick Astley';
</script>

<img src="{src}" alt="{name} dances.">
<img src={src} alt="{name} dances."> <!-- 引用符を省略 -->
<img {src} alt="{name} dances."> <!-- 省略記法 -->

(引用符の省略や省略記法はこの記事では意図的に使用しないことがあります。このブログはSvelteのシンタックスハイライトが効かないため見やすさのためHTMLシンタックスを使用しており、引用符を省略するとHTMLシンタックスが壊れ、記述が見にくくなるためです。ご了承ください)

双方向データバインディングをするにはbind:属性名のような記述をします。
次の例は<input>に入力した値がname変数に与えられる例です。

<script>
let name = 'world';
</script>

<input bind:value={name}>
<h1>Hello {name}!</h1>

bind:にも条件によって省略記法がありますが割愛します。気になる方はチュートリアルを見てみてください。

参考:

イベントハンドラ

イベントハンドラを登録するにはon:イベント名のような記述をします。次の例は<button>のクリックイベントにhandleClick関数を登録する例です。

<script>
let count = 0;

function handleClick() {
count += 1;
}
</script>
<button on:click={handleClick}>
Clicked {count} {count === 1 ? 'time' : 'times'}
</button>

Svelteにはbind:on:のように:で区切った属性を使用する記述方法(ディレクティブと呼ばれます)がいろいろとあります。

参考:

ifブロック

テンプレートで、条件によって変化させたい記述をするには{#if}ブロックを使用します。

<script>
let loggedIn = false
</script>

{#if loggedIn}
<button on:click="{()=>loggedIn=false}">Log out</button>
{:else}
<button on:click="{()=>loggedIn=true}">Log in</button>
{/if}

Svelteにはほかにも{#each}ブロックを使用した反復表示や{#await}ブロックを使用したPromiseの状態別の表示などがあります。

参考:

コンポーネントを使用

別の.svelteで作成したコンポーネントを使用するにはimportしてタグのように記述するだけです。

<script>
import MyComponent from './MyComponent.svelte';
</script>

<MyComponent/>

リアクティブ

テンプレートが変数の値の変化によって変化するのはこれまでの結果でもわかったと思いますが、スクリプトでも特殊な記述によって、変数の変化に応じて処理したり、変数の結果に応じて値の変わる変数を宣言したりできます。
この記述には$: というような記述をします。

次の例は変数の結果に応じて値の変わる変数を宣言する例です。$: 変数のような記述をします。doubledには常にcountの2倍の値が格納されます。Vueの算出プロパティと似ています。

<script>
let count = 0;
$: doubled = count * 2;
</script>

<button on:click="{()=>count += 1}">+1</button>
<p>{count} doubled is {doubled}</p>

変数の変化に応じて処理を行いたい場合は$: ステートメントのような記述をします。$の後に処理したいステートメントを記述すると、ステートメントで使用した変数が変化したタイミングで処理されます。VueのwatchEffectに似ています。
次の例はcount変数の変化に応じて処理を実行する例です。10回ボタンをクリックするとメッセージが表示されます。

<script>
let count = 0;
$: {
if (count >= 10) {
alert(`count is dangerously high!`);
count = 9;
}
}
</script>

<button on:click="{()=>count += 1}">+1</button>
Num of clicked: {count}

if文はステートメントなので$:の後の{}は書かなくても同じです。

$: if (count >= 10) {
alert(`count is dangerously high!`);
count = 9;
}

参考:

SvelteのライフサイクルフックとSvelteの機能を使用した構文

今まで紹介した記述はsvelteをライブラリとしてインポートすることはありませんでした。次はsvelteをライブラリとしてインポートして使用する機能や構文を紹介します。

ライフサイクルフック

コンポーネントが最初にDOMとしてレンダリングされた後(マウント)に何かの処理をしたり、コンポーネントが破棄されたときに何かの処理をしたい時は、ライフサイクルフックを使用します。

次の例はマウント時に処理をする例です。'svelte'からimportしたonMountを使用します。マウントされたタイミングでメッセージが表示されます。

<script>
import { onMount } from 'svelte';
onMount(() => {
alert('Hello!');
});
</script>

ライフサイクルフックにはonMountbeforeUpdateafterUpdateonDestroyがあります。

参考:

ストア

Svelteはストアという機能を持っています。これは簡単に言えば状態管理の機能です。Vueでいうところのrefオブジェクトなんかが機能的には近いのではないでしょうか?

Svelteは変数などの値の変更がリアクティブに反応してDOMなどが更新されることは今までの説明でなんとなくわかると思いますが、今までの方法では.svelteファイルの外で管理されている値(状態)には反応できません。値(状態)を複数のコンポーネントで共有したりする場合はSvelteのストアの機能を使用します。

ストアを作るには'svelte/store'からインポートできる関数を使用します。
詳細は割愛しますが、次のコードではcountというストアを作成しました。

/* stores.js */
import { writable } from 'svelte/store';

export const count = writable(0);

.svelteでストアを使用する際はストアの名前の頭に$をつけた変数名を使用します($を使用しない方法もありますが便利なのは$を使用する方法です)。
次の例ではボタンクリックによってcountストアを1増やすコンポーネントです。実際にcountストアを1増やしているのは$count++と書いた部分です。

<!-- Incrementer.svelte -->
<script>
import { count } from './stores.js';
function increment() {
$count++
}
</script>

<button on:click={increment}>+1</button>

ストアの値を参照する場合も同じように$をつけます。次の例はテンプレート部分で$countと記述することでcountストアの値を参照しています。実際に動かしてみるとIncrementer.svelteでの値の更新に反応して、表示が変化することを確認できます。

<!-- App.svelte -->
<script>
import { count } from './stores.js';
import Incrementer from './Incrementer.svelte';
</script>

<h1>The count is {$count}</h1>
<Incrementer/>

参考:

アニメーション

Svelteはtransition:namein:nameout:nameanimate:nameのディレクティブを使用してさまざまなアニメーションを実現できます。

おそらく説明するよりは実物を見てもらったほうがよいと思うのでこれとかサンプルをぜひ見てみてください。おもしろいアニメーションを作るためだけにSvelteを学ぶのも良いかもと思うぐらい、Svelteだけでいろいろなアニメーションが作れます。

チュートリアルはこのあたりから始まります。


構文や機能についての紹介はこのあたりで終わりにしますが、まだまだ紹介していない便利な機能がたくさんあります。さらに詳細を知りたい方はチュートリアルを覗いてみてください。

Svelteのコミュニティ

海外ではSvelte SocietyというSvelteのコミュニティがあります。

このサイトの中にはSvelteで使えるライブラリがたくさん載っていたりSvelteとこれを組み合わせるにはどうしたら良いか?などのレシピ集があったりと、Svelteを本格的に始めるには重要な情報が多くあるので、必ずチェックすべきサイトです。このコミュニティのチャットはDiscordにあります。もしドキュメントを読んでもわからないような質問があれば、投稿してみるともしかしたら誰かが答えてくれるかもしれません。

日本にも「Svelte日本」というSvelteのコミュニティがあります。Svelte日本 はこれまでリンク先として紹介してきた日本語翻訳にも取り組んでくれています。こちらのチャットもDiscordにあります。Svelte日本 のDiscordには、困ったときの「help」チャンネルもありますが、週3回Svelteに関するクイズが出題される「svelteクイズ」という楽しく学べるチャンネルがあったりします(ただし先週から休止中。過去の問題を見ることはできます)。

参考:

Svelteのイベント

Svelte Societyイベントがまとめられているページがあります。
最近行われたSvelte Summit Spring 2021と前回のSvelte Summit 2020についてはまとめ記事を書いてくださった方がいますのでリンクを紹介しておきます。

感想

REPLのおかげか、とにかく入門するのが楽でした。ちょっと時間ある時にブラウザで開いてコード書いて動かせるので本当に楽です。

Svelteの構文についてはコードをスマートに書けるよう設計されているのがよくわかりました。Svelteを使えばコードがスッキリして気分良くコーディングできそうです。

ライブラリやツールなどはほかの有名どころフレームワークと比べるとまだ成熟していないようにも見えますが、主要なツールのサポートはおおむねそろっている印象を受けました。

公式リポジトリでエディタのサポートPrettierプラグインがメンテナンスされていたりするので、もし何か足りないことが見つかってもSvelteコミュニティと協力できればすぐに実現されそうな期待があります。

参考にした参考になるサイト(リソース)