SvelteとReactの基本を比較
読む所要時間: 24 分
JavaScriptフレームワークについて語る時、Svelteは新顔です。これまでよく噂を耳にしてきましたが、今回試用し、Reactと比較します。
この記事では、SvelteとReactの違いについて検討し両方のフレームワークを用いて基本的なアプリを構築する方法を紹介します。目標は、以下を取り扱う、中心的概念をデモンストレーションすることです。
- 構造化コンポーネント
- 初期化ステート
- propsの受け渡し
- ステートのリフト
- イベントリスナー
- 動的なスタイル
さらに、条件付きレンダリング、ライフサイクルメソッドなど、他にも話すべきトピックは多数ありますが、この記事では基本に焦点を置き、その中で私の感想も述べていきます。
前提条件
このチュートリアルを開始するに当たり、以下のツールと技術が必要です。
- npmまたはyarn
- Node.jsがマシンにインストールされていること
- VS Codeを使用している場合には、このSvelte拡張機能
Svelte対React
SvelteとReact.jsは、Webアプリケーション開発向けかつ、コンポーネントベースのJavaScriptフレームワークです。この2つの間の主な違いは、Svelteは仮想DOMを使用しないことです。Svelteはビルド時にコードをプレーンなJavaScriptにコンパイルするのに対し、Reactは実行時にコードを解釈します。
以下は、Svelteドキュメントから引用したものです。
Svelteは、ユーザーインターフェイスを構築するための急進的な新しいアプローチです。ReactやVueのような従来のフレームワークは、ほとんどの作業をブラウザで行いますが、Svelteは、その作業を皆さんがアプリを構築するときのコンパイルステップに移動します。
仮想DOM比較などのテクニックを使用する代わりに、Svelteはアプリの状態が変化すると、DOMを更新するコードを記述します。
いいですね。しかし、この調査においては、背景の詳細は重要ではありません。私は、これを開発者体験の観点から検討します。SvelteとReactでの開発とは、どのようなものでしょうか。使用していて気持ちの良いものでしょうか。楽しいでしょうか。直観的でしょうか。
見てみましょう。
アプリの準備
この比較チュートリアルでは、以下の要件を持つ、小さなアプリを構築します。
- 3つのコンポーネント:
App
、Heading
、Button
- ボタンがクリックされるたびに、見出しが更新され、現在のクリック数を表示
- ボタンがクリックされるたびに、ボタンの色が変化
以下のコマンドを使用し、svelte-reactという新しいディレクトリをコンピューターに作成します。
次のステップは、SvelteアプリとReactアプリを準備し、ローカルで実行することです。Svelteの設定プロセスは、Reactより1ステップ多くなっています。さらに、アプリが実行されるポートは、SvelteはPORT=5000
ですが、ReactはPORT=3000
です。
Svelte
ターミナルまたはコマンドプロンプトウィンドウで、以下のコマンドを実行します。
React
2つ目のターミナルを開き、svelte-reactディレクトリに戻ります。ここから、以下のコマンドを実行します。
Svelteコマンドの実行は先ほどのReactに比べて短時間で終了したと感じるでしょう。これはSvelteでは実際にユーティリティを実行するのではなくテンプレートを複製しているためです。
アプリコンポーネントの構築
上記のコマンドを実行した後は、SvelteとReact両方のスターターアプリに、すでに多くのファイルとコードがあることが分かります。
SvelteとReact両方のプロジェクトのコンポーネントファイルは、/srcフォルダ内にあります。表示された/srcフォルダの中を見ると、Svelteファイルは拡張子.svelteで終わることが分かります。Reactコンポーネントファイルは.jsで終わります。
App
コンポーネントがある各スターターアプリは、svelte-react/svelte-test/src/App.svelteとsvelte-react/react-test/src/App.jsにあります。好みのコードエディタでこれらのファイルを開き、両方の中にあるコードを削除します。この後の記事で新しくコードを実装します。
コンポーネント構造
Svelte
Reactコンポーネントとは異なり、Svelteコンポーネントでは、昔のHTML、CSS、JavaScriptのような形式でコードを記述できます。
コンポーネントのすべてのJavaScriptは、ファイルの一番上にある<script></script>
タグの内側に収めます。
<script></script>
タグの下には、コンポーネントのHTMLを記述できます。そう、通常のHTMLドキュメントと同様です。そうなんです。
次に、HTMLの下で、<style></style>
タグの内側にスタイルを追加します。各コンポーネントのスタイルは、そのコンポーネントのみに適用されます。つまり、例えば、それぞれのコンポーネントで<p>
タグのスタイルを設定でき、インポート時にスタイルが互いにオーバーライドされません。
App.svelteの手始めにファイル内のすべてを削除し、空の<script>
タグをいくつか追加します。
コンポーネントのコードの大半は、これらの<script>
タグの内側に挿入されます。
React
ReactアプリでApp.jsファイルを開き、こちらもコンテンツを削除し以下を追加します。
このコードは、App()
と呼ばれる基本機能コンポーネントを作成します。それをエクスポートします。ここでのSvelteとReactのもう1つの主な違いは、Svelteではコンポーネントをエクスポートしないことです。
インポート
前述のように、このアプリには、App
、Heading
、Button
の3つのコンポーネントがあります。SvelteアプリとReactアプリの両方で、Heading
コンポーネントとButton
コンポーネントは、App
にインポートされるため、それらをApp
コンポーネント内の子コンポーネントとして使用できます。後でこれらのコンポーネントを記述しますが、ここでは、App
コンポーネントを構築する際に、それらを参照することを覚えておいてください。
Svelte
Svelteで、インポートを<script>
タグの内側に追加します。ハイライトされた行を反映させまするよう、App.svelteファイルを編集します。
React
Reactへのインポートは、ファイルの一番上、関数(またはクラス)コンポーネントの間に配置されます。App.jsの一番上、App()
関数の前に、以下のコードを追加します。
Reactの場合には、useState
フックもインポートします。これは、App
がステートフルなコンポーネントであるためです。Svelteではこのフックのアナログ値はありません。何もインポートする必要はありません。
stateの初期化
App
は、ステートフルなコンポーネントです。これは、color
とcount
の2つのステート値を持ちます。
color
はボタンの色です。この値は、propsとしてButton
コンポーネントに渡され、Button
コンポーネントがクリックされるたびに変化します。初期化される際の値は#000000
です。これは、黒色を示す16進数コードです。
count
はButton
がクリックされた回数を表します。初期化される際の値は0
です。
Svelte
Svelteでは、変数を割り当てることによりステートが作成されます。インポートの下で、<script>
タグの間に、以下のステート宣言を追加します。
さらにSvelteでは、リアクティブ宣言が提供されており、ステート値を再計算することができます。これを使用する必要はありませんが、変化する他のステート値から派生するステート値を使用する場合に非常に便利です。
ここで覚えておくべきは、SvelteでのDOMの更新はstate変数の割り当てによりトリガされることです。ステートにアレイやオブジェクトが含まれる場合には、.push()
のようなメソッドで更新しても、DOMの更新はトリガされません。Svelteでのこの更新の管理方法の詳細については、配列とオブジェクトの更新に関するドキュメントをご覧ください。
React
useState
フックはすでにインポートしました。今度はそれを実際に使用します。
App()
関数内の、App.js内に、以下のステート宣言を追加します。
このコードでcount
と呼ばれるstate変数が、初期値0
と、その値を更新するメソッドsetCount()
と共に作成されます。同様に、color
と呼ばれるstate変数も、初期値#000000
と、その値を更新するメソッドsetColor()
と共に作成されます。この時点で、Svelteのステート初期化は、読み込みも使用も非常に簡単です。
コンポーネントをレンダリングし、propsを渡す
いずれのプロジェクトでも、目標は、<main>
エレメントから構成されるユーザーインターフェイスを作成することです。そのエレメントが2つのネストされたコンポーネントHeading
とButton
をラップします。
App
コンポーネントは、これらの子コンポーネントの両方にpropsを渡します。handleClick()
というハンドラー関数に加え(この関数はこの後で記述します)、Heading
コンポーネントはcount
ステート値を受け取り、Button
コンポーネントはcolor
ステート値を受け取ります。
Svelte
Svelteは独自のテンプレート作成言語を使用することにより、ReactはJSXを使用することにより、ユーザーインターフェイスを作成します。Svelteのテンプレート作成言語は、Flask、PHP、Liquidのように、サーバー側環境でのHTMLテンプレート記述に非常に似ています。この記事では詳細を取り扱いませんが、素晴らしいのは、直接ファイル内で<script>
タグの下にHTMLを記述できることです。
ハイライトされているコードをコピーし、App.svelteファイルの、<script>
タグの下に貼り付けてください。
React
App.jsに戻り以下のコードをコピーし、App()
関数の内側に実装されているステート宣言の下に貼り付けます。
このコードは、App()
関数から、ユーザーインターフェイスのJSXを返します。
これが、SvelteとReactが最も類似している部分です。propsの渡し方が完全に同じです。この例では、Svelteのテンプレート作成は、ReactのJSXと完全に同じに見えます。
Svelteに興味を持つReact開発者であれば、propsの渡し方について驚くことや目新しいことはありません。一方、propsの受け取り方については、Svelteは少し異なります。これについては、この記事で後述します。
ステートのリフト
このアプリを機能させるには、Button
がクリックされるたびに、App
コンポーネントのcount
ステート値が増加する必要があります。つまり、子コンポーネントから親コンポーネントにデータをリフトアップするメカニズムが必要です。
皆さんは、handleClick()
関数をButton
コンポーネントにpropsとして渡すことにより、このプロセスをすでに開始しています。
これから記述するこの関数は、App
コンポーネントに属します。この関数をpropsとして子Button
コンポーネントに渡すと、Button
コンポーネントはこの関数にアクセスでき、ボタンがクリックされるたびに呼び出すことができます。この方法により、App
コンポーネントは子コンポーネントで何が起きているかを把握できます。
この関数は、App
コンポーネントのcount
とcolor
のステート値を更新します。
Svelte
以下のコードをコピーし、App.svelteの<script>
タグ内側のステート宣言後に貼り付けます。
React
以下のコードをコピーし、App.jsの、App()
関数内のreturn
ステートメントのすぐ上に貼り付けます。
Reactでは、ステート値を更新するために、前に宣言したsetCount()
メソッドとsetColor()
メソッドを使用する必要がありますが、Svelteでは直接更新できます。
これでApp
コンポーネントでの作業は終わりです。次に、Heading
コンポーネントに移ります。
Headingコンポーネントの構築
Heading
コンポーネントは、アプリの見出しテキストとクリックカウンターを表示します。これはステートフルなコンポーネントではなく、ボタンが何回クリックされたかを示す、1つのデータ(count
)をpropsとして受け取ります。
Svelteプロジェクトのsrcフォルダ内に、Heading.svelteという名前の新しいファイルを作成します。
Reactプロジェクトのsrcフォルダ内に、Heading.jsという名前の新しいファイルを作成します。
propsを受け取る
Svelte
新しいHeading.svelteプロジェクト内に、以下のコードをコピーして貼り付けます。
<script>
タグ内側のハイライトされた行を見てください。この行は、コンポーネントがcount
というpropを受け取ることを、Svelteに示します。
この結果、上記の6行目に見られるように、count
をHeading
コンポーネントのHTMLテンプレート内で直接使用できます。
この方法での記述は少し通常と異なりますが、ファイルの一番上で直ちにpropsを宣言すると、見た目が整理され、すぐにpropを使用でき、気持ちがよいものです。
React
新しいHeading.jsファイルに切り替えます。以下のコードをコピーし、このファイルに貼り付けます。
このコードにより、新しい機能的なコンポーネントであるHeading
が作成されます。これは{ count }
パラメータを1つ持ち、このパラメータはcount
値であり、コンポーネントに渡されたprops
オブジェクトから非構造化されたものです。
Buttonコンポーネントの構築
Button
コンポーネントは、UIで<button>
エレメントをレンダリングします。このコンポーネントは、color
(<button>
エレメントのスタイルに使用される)とhandleClick()
(<button>
エレメントがクリックされると呼び出される関数)の2つのpropsを受け取ります。
Svelteプロジェクトのsrcフォルダ内で、Button.svelteという名前の新しいファイルを作成します。
Reactプロジェクトのsrcフォルダで、Button.jsという名前の新しいファイルを作成します。
イベントをリッスンする
クリックやその他のマウスイベントのようなインタラクティブなイベントのリスニングは、SvelteとReactでは少々プロセスが異なります。このセクションでは両方をデモンストレーションします。
Svelte
以下のコードをコピーし、Button.svelteに貼り付けます。
このコードでは、両方のpropsは、ファイルの一番上にある<script>
タグ内で宣言されます。
次に、<button>
エレメントが作成されます。6行目に新しい構文があることに気付くと思います。現時点ではこのスタイル部分は無視してください。後で説明します。ここでは、クリックイベントのリスナーの構文に注目してください。従来のものとは異なる構文です。
Svelteは、on:
ディレクティブを使用することにより、DOMエレメントにイベントリスナーを追加します。さらにSvelteには、便利なイベント修飾子があります。これを使用すると、例えば、最初のクリックでクリックイベントを1つトリガするだけにすることが可能になります。また、独自のコンポーネントイベントをディスパッチすることもできます。
React
以下のコードをButton.js内に追加します。
サーバーがまだ実行中で、この時点でエラーが発生する場合には、心配しないでください。styles
オブジェクトを追加すると、エラーが修正されます。
上記のコードにより、Button()
という新しい機能コンポーネントが作成されます。これは1つの引数(props
)を受け取り、propsはcolor
とhandleClick
に分解されます。handleClick()
関数はhandleClick
props上で利用でき、標準のインラインonClick
イベントを<button>
エレメントJSXで使用できます。
動的なスタイル
このアプリでは、Button
コンポーネントは、カラーをpropsとして受け取ります。このカラー値は、<button>
エレメントの背景色になります。
Svelte
動的なスタイルはもちろんSvelteで実現可能ですが、想像したほどシンプルではありませんでした。
Svelteでは残念ながら、<style>
タグ内で直接propsの値を使用することはできません。ただし、コンポーネントのHTMLを、JavaScriptとCSSの間でコミュニケーションする方法として使用できます。
以下のスタイルをコピーし、Button.jsで、ファイルの最後のButton
コンポーネントのHTMLの下に貼り付けます。
background-color
スタイルプロパティは、color
propsを直接参照していませんが、color
というスタイル変数を参照しています。これは、前のセクションにおいて、HTMLで<button>
エレメントに作成したものです。
これは少し手間がかかりますが、その代わりに、ローカルでスコープされる実際のCSSを記述できます。(心配しないでください。グローバルCSSも使用できます)。
React
Reactでは、何種類もの方法でコンポーネントにスタイルを追加でき、動的スタイルには特にインラインスタイルが最も一般的に使用されます。
JSXでインラインスタイルを使用するには、スタイルでオブジェクトを作成し、次にそのオブジェクトをエレメントのstyle
属性に割り当てます。オブジェクトの割り当ては前のセクションで行いました。
以下のコードをコピーし、Button()
関数内の、return
ステートメントの前に貼り付け、styles
オブジェクトを作成します。
アプリをテスト
すべてのファイルを保存し、閉じます。プロジェクトサーバーが実行されていない場合、ここで再起動します(Svelteではnpm run dev
、Reactではnpm start
)。次にブラウザタブの1つでlocalhost:5000にアクセスし、次のブラウザタブでlocalhost:3000にアクセスします。
各アプリが実行されたら、ボタンをクリックし、見出しにカウントの更新が表示されボタンの色が変化するかどうかを確認します。
Svelte
React
SvelteとReactは、マージン、パディング、ボタンのネイティブスタイルにいくつかの違いがありますが、全体としては、2つの異なるフレームワークに、2つの同じ機能を持つアプリを構築できます。どう思われましたか?作業はどうでしたか?
まとめ
このSvelteの調査は、非常に興味深いものでした。これまでのところ、SvelteとReactの機能は類似しているようです。Svelteのテンプレート作成言語は、特にon:
ディレクティブが非常に魅力的です。正直なところ、テンプレート化されたHTML記述が懐かしいです。今後もSvelteで構築を続け、Reactでは困難な、ライフサイクルメソッドやデータバインディングのような高度な概念をさらに検討していきたいと思います。
Svelteでの作業のご感想を、Twitterでお聞かせください!
Ashleyは、TwilioブログのJavaScriptエディターです。Ashleyと協力し、Twilioにテクニカルストーリーを紹介するには、Twitterで@ahl389までご連絡ください。TwitterでAshleyが見つからない場合は、どこかのパティオでコーヒーを飲んでいることでしょう(ワインの時間かも)。