はじめに
本記事は、ユーザーインターフェイスを構築するためのJavaScriptフレームワークのひとつ「Svelte」を紹介する連載の第2回です。
連載初回となる前回は、Svelteの生まれた背景や全体的なコンセプトを取り上げました。今回は、より具体的なSvelteでのコードの書き方を解説し、実際にSvelteで簡単な単一ページアプリケーション(以下SPAと書きます)を構築する手順を説明していきます。
前半では、実際に手を動かしてSvelteのコードを書いてみる前に、Svelteのコードの全体的な構造を頭に入れておきましょう。前半は「初歩的なHTML/JavaScriptやjQueryでの開発の知識はあるが、モダンフロントエンドでの開発はほとんど(まったく)ない」方を想定して解説します。
後半では、Svelteでの開発の流れや、コマンドラインでのプロジェクトの作成方法を具体的に説明していきますが、ややもすると何も考えずに書き写して終わりになってしまう場合があります。
特に、Svelteを機会にフロントエンド開発をしっかり身につけたいと思われる方は、実行するコマンドや、書き写すコードの背後にあるモダンフロントエンド開発の考え方をぜひ頭に入れながら読み進めてください。それだけで身に付く深さがぐっと増すはずです。
逆にReactやVueなどである程度フロントエンド開発の経験がある方は、後半までスキップしても問題ありません。
Svelteにおけるコンポーネントの概要
コンポーネントの構造
ReactやVueなどの先行するフレームワーク・ライブラリと同様に、Svelteでは、アプリケーションを「コンポーネント(components)」という単位に分割して管理します。単純なコンポーネントの例を見てみましょう。
// Counter.svelte <script lang="ts"> import AnotherComponent from './Another.svelte'; export let endOfSentence = 'してください。'; let count = 0; const handleClick = () => count += 1; </script> <style> p { padding: 1em; } </style> <div> <p>クリックした回数: {count}</p> <button on:click={handleClick}>クリック{endOfSentence}</button> </div> <AnotherComponent />
見ての通り、ほとんど通常のHTMLで慣れ親しんだ書き方しか登場しません。目新しいのは{}
やon:click
くらいです。
一方で、HTMLとしては断片に過ぎないこれらのコードが、単一のファイルに格納されるのには違和感があるかもしれません。また、そのファイルの拡張子も.svelte
という見慣れないものとなっています。
Svelteでは、この.svelte
拡張子を持つファイル1つが、1つのコンポーネントを表します。.svelte
ファイルは、SvelteコンパイラによりJavaScriptの「モジュール」にコンパイルされ、他のJavaScriptからインポートすることができます。
UI開発では、どこかで開発したUI要素を他のページでも使い回したくなることが良くあります。例えば「表示内容をクリップボードにコピーするボタン」などは、毎回実装するのは大変ですよね。Svelteでは、こうしたUI要素を「コンポーネント」という単位に切り出しておき、必要に応じて各ページでインポートして再利用する、ということが簡単にできます。
詳細は後述しますが、コンポーネントに切り出した部分は、コンポーネントの外側から変更できる範囲が限定されます。そのため「この変数を変更すると他の部分に影響する」とか「この行を削除するとなぜか他の部分が動かなくなる」といったような問題が起きづらくなります。また、1つのHTMLファイルやJavaScriptファイルが大きくなると、このような関係性を把握する認知的な負荷が大きくなりがちです。コードベースをコンポーネントに切り出すことで、こうした問題を予防する効果もあります。
コンポーネントは、ほとんど通常のHTMLの感覚で書くことができますが、<script>
と<style>
タグだけは少し特殊な扱いとなります。
<script>
タグには、ほとんど通常と同じ感覚でJavaScriptを書くことができます。ここに書いたJavaScriptは、コンポーネントがインスタンス化されるときに実行されます。何点か、通常と異なる扱いになる文法があります。これらについてはこの後すぐに解説します。また、<script>
タグは例外を除いて1つのコンポーネントに1つしか書くことができません。
<style>
タグには、通常のCSSを書くことができます。ただし、ここに書いたスタイルシートは原則として、そのコンポーネントにしか適用されません。そして<style>
タグも、1つのコンポーネントに1つしか書くことができません。
まとめると、Svelteコンポーネントは基本的に「動的処理(JS)」+「HTML断片」+「スタイルシート」から構成されることになります。
テンプレート構文と内部状態(ステート)
SvelteコンポーネントがHTML文書の中に配置されると、その部分がSvelteコンポーネントの「HTML断片」と置き換えられます。
このとき、{}
で括られた部分は、その中身の評価値で置き換えられます。例に登場する{count}
であれば、<script>
タグ内のJavaScript(以下、単にJSと書きます)でlet
で宣言されているcount
変数の値で置き換えられます。最初の段階では、初期値である0
となります。
JS内でcount
変数が変更されると、HTML文書内の{count}
も同じように更新されます。もしhandleClick
関数が呼ばれればcount += 1
が実行されてcount
の値は1
になりますね。このときに、自動的に{count}
と書かれた箇所も1
に更新されます。
この例のように、コンポーネントの内部状態の変化に応じて、表示内容が自動的に更新されることを指して「リアクティブである」「リアクティビティ(reactivity)がある」などと呼びます。コンポーネントの内部状態は「ステート(states)」などと呼ぶこともあります。
Svelteでは、let
で宣言された変数がステートを表すことになります。
プロパティ
ステートはコンポーネントの内部状態なので、コンポーネントの外側からは変更したり読み取ったりすることができません。
もしかすると、この制限は不便に聞こえるかもしれません。確かに、素朴なHTML/JavaScriptやjQueryでの開発で頻繁にやっていたように、必要な変数にいつでもアクセスして状態を変更できたほうが便利な面もあります。
一方で、この自由さのメリットは、アプリの規模が大きくなるにつれて徐々にデメリットに変化していきます。ある段階で、「この変数の値を変更できて便利だ」と思う場面より、「この変数に基づいて処理を進めたいが、もしかしたらどこかで変更しているのを忘れているかもしれない」と心配になる場面が多くなってきます。この段階は、1人で趣味的なプログラムを書いている場合でさえ、意外なほど早く訪れます。複数人で開発する本格的なプロジェクトであれば尚更です。
モダンフロントエンドの開発では、この自由を制限し、その代わり「コンポーネントのコードを書くときには、そのコンポーネントの内部状態だけを把握すれば十分」となるようにしています。
といっても、内部状態に一切アクセスできなければ、実用的なソフトウェアを開発することは難しくなります。そこで、コンポーネントの状態に外部からアクセスする窓口として「プロパティ(properties/props)」という仕組みを備えることが一般的です。
Svelteでは、export let
で宣言した変数がプロパティとなります。今回の例ではexport let endOfSentence = 'してください。';
の行がこれにあたります。使い方は詳しくは後半で説明しますが、Counter
コンポーネントを次のように書いて呼び出すことで、呼び出し側からendOfSentence
変数の値を変更することができます。
<Counter endOfSentence='してね。' />
こうすると、ボタンに表示される文字列は「クリックしてください。」から「クリックしてね。」に変化します。endOfSentence
プロパティを通じて、コンポーネントの内部状態をコンポーネントの外から書き換えたことになります。
これに対して、以下のようにしてもcount
の値は変化しません。count
はexport let
ではなくlet
で宣言された純粋な内部状態であり、プロパティとは違って外部からアクセスできないことに注目しましょう。
<Counter count={127} />
また、このexport
の使い方はJavaScript本来のexport
構文とはあまり関係がないことに注意してください。Svelteはこのように、JavaScriptの文法の持つ意味(セマンティクス)を変更しています。これは、Svelteがコンパイラだからできることです。
この書き方は、JavaScriptにとても詳しい方には初見では違和感があるかもしれません。コンポーネント開発の文脈においては十分自然に感じるように工夫して変更されているので、慣れてしまえば気になりません……が、JavaScriptにまだ慣れていない方は、Svelteの外でJavaScriptを書くときに混乱にしないように意識しておくと良いでしょう。
さて、以上でSvelteコンポーネントの基本的な構成要素と構造を把握していただけたと思います。他にもいくつかアプリケーションを開発するために重要な要素があるのですが、ここからは実際にSvelteのコードを書きながら、どんな場面で使いたくなる機能なのか、どんな挙動の機能なのかを体感しながら身につけて頂きたいと思います。