フロントエンドエンジニアの氏家です。
最近、JavaScript のフレームワークであるSvelteに関する記事を多く見かけるようになりました。 僕の中で「何か新しいフレームワークが出てるなぁ」から「面白そうだし触ってみよう」という気持ちに変わったので、vivit の数多くのプロダクトで採用している React と新進気鋭な Svelte を比べてみて、何がどう違うのか体系的に学んでいこうと思います。
そもそも Svelte とは
https://svelte.jp/ では以下のように説明されています。
Svelte はユーザーインタフェースを構築する先鋭的で新しいアプローチです。React や Vue のような 従来のフレームワークがその作業の大部分を ブラウザ で行うのに対し、 Svelte はその作業を アプリをビルドする際の コンパイル時 に行います。
Svelte は仮想 DOM による差分検出のようなテクニックを使用する代わりに、 アプリケーションの状態が変化したときに DOM を外科的に更新するコードを生成します。
「 Svelte はコンパイラである」とも言われており、記述したコードをプレーンな (vanilla) JavaScript にコンパイルします。そのため、バンドルサイズやメモリ使用量が最低限で済み、アプリケーションのロードや実行を高速化してくれます。
React と Svelte のコードの違い
百聞は一見にしかず、 React のコードと Svelte のコードを比べてみましょう。
作成するのは<Button name="hoge" />
で呼び出して使うことができる、クリックするたびにカウントされるボタンコンポーネントです。
React
まずは React のコードを書いてみます。 今回は vivit のプロダクトでも採用している、@Takepepeさんの経年劣化に耐える ReactComponent の書き方に習い記述していきます。
/Button.tsx import { useState } from "react"; import styled from "styled-components"; export type ContainerProps = { name: string; }; type Props = { className?: string; count: number; handleClick: () => void; } & ContainerProps; const Component = ({ name, className, count, handleClick, }: Props): JSX.Element => ( <div className={className}> <button className="button" onClick={handleClick}> {name}が{count}匹 </button> </div> ); const StyledComponent = styled(Component)` padding: 10px; .button { color: #333; } `; export const Button = (props: ContainerProps): JSX.Element => { const [count, setCount] = useState(0); const handleClick = () => { setCount(count + 1); }; return <StyledComponent {...props} count={count} handleClick={handleClick} />; };
このぐらい単純だと分かりやすいですね。 それぞれの層で何が行われているかもすぐに理解することができます。
Svelte
次にこれを Svelte で書いてみます。
/Button.svelte <script lang="ts"> export let name: string = ""; let count: number = 0; const handleClick = () => { count++; }; </script> <div class="wrapper"> <button class="button" on:click="{handleClick}">{name}が{count}匹</button> </div> <style lang="scss"> .wrapper { padding: 10px; .button { color: #333; } } </style>
コード量が React の半分になりました。 さらに、 Svelte のコードを見慣れない方でもこのコードが何をやっているか一目瞭然です。 書き方は Vue.js に似ているかもしれません。
もう少し詳しく見ていきます。
Script
<script lang="ts"> export let name: string = ""; let count: number = 0; const handleClick = () => { count++; }; </script>
ここではモジュールやコンポーネントを import したり、コンポーネントの props を定義したり、 React でいう hooks を使用したりします。
props の定義をexport
と書くのは違和感があるかもしれませんが、すぐに慣れてしまいます。
コードから分かると思いますが、状態管理を単なる変数で行っているのも Svelte の特徴の一つですね。
また、 Svelte は正式に TypeScript をサポートしているので、開発体験が損なわれることもありません。
DOM
<div class="wrapper"> <button class="button" on:click="{handleClick}">{name}が{count}匹</button> </div>
ここでは DOM(HTML テンプレート)を定義しています。 初めからロジックや状態管理のコードが分離されているので、レイアウトのみの Stateless なレイヤーになっています。
Style
<style lang="scss"> .wrapper { padding: 10px; .button { color: #333; } } </style>
Svelte は SCSS もサポートしており、 React で StyledComponents を使用して開発していた方は同じようにスタイリングすることができます。
ここで書いたスタイルは完全にコンポーネントに閉じており、例えば以下のように、
div { padding: 10px; }
と書いたとしても、親コンポーネント(や子コンポーネント)で定義されたdiv
タグにpadding: 10px
が適用されることはありません。
比べて感じたこと
まずはじめに感じたのは、最近のモダンなフレームワークの中では学習コストが低いということです。 私はフロントエンドエンジニアとして開発に携わる際に、様々な技術や知識を勉強しました。 特に React では JSX や hooks 、 CSS in JS などの概念を覚えるのに苦戦した記憶があります。
それに比べて Svelte は、<script>
で囲まれた単純な JavaScript 、<style>
で囲まれた単純なCSS、レイアウトをつくるHTMLのみで構成されており、状態管理の方法について覚える必要も、複雑なライブラリを導入する必要もありません。
HTML、CSS、JavaScriptさえ書ければ、すぐにでも Svelte で開発できます。
もう1つ感じたのは、開発体験が抜群に良いということです。 主に以下の点が挙げられます。
- TypeScript や SCSS をサポートしている
- コード量が少ないうえに、直感的に書ける
- ビルド(コンパイル)、実行速度が早い
- VS Codeの拡張機能「Svelte for VS Code」が良い
- シンタックスハイライトやインテリセンスなど
特にコードの記述量を減らせる点はとても大きかったです。 Svelte 独自の記法などはあるものの、シンプルで分かりやすいため可読性が大幅に上がり、実装コストも下がります。
まとめ
ここまで React と Svelte の違いについて書きましたが、どちらが優れいている、というわけではありません。 どちらのフレームワークにもメリット、デメリットが存在しており、プロダクトやチームで行われる技術選定によって適切なものを使用すべきだと思います。
しかし、個人的な感想としては Svelte による開発の方が、 React に比べて「分かりやすくて、楽しい」と感じました。
実際、State of JS 2020で満足度・関心のランキングで Svelte が1位を獲得しています。
おわりに
vivitではまだ Svelte を使用した開発は行っておりませんが、ちょっとしたLPなどの制作であれば積極的に採用していきたいです。
また、SvelteKitが正式にリリースされればより大規模なアプリケーション開発に採用していけると思うので楽しみです。
今は逸る気持ちを抑えて、個人で Svelte 開発を楽しみたいと思います。
vivit株式会社ではモダンな環境でアウトドア事業を成長させていくエンジニアを募集しています。 また、23卒の新卒エンジニア採用も行っていますので、詳しくは下記をご覧下さい。