Skip to main content

Sapper: 理想の Web アプリケーションフレームワークを目指して

次の一歩を踏み出す

せっかちな人のためのクイックスタート: Sapper のドキュメントスターターテンプレート

もし完璧な Node.js の Web アプリケーションフレームワークの特徴を挙げるとしたら、次のようなものを思いつくでしょう:

  1. 最初の読み込みの高速化や SEO 対策に支障をきたさないために、サーバーサイドレンダリングを行う
  2. 当然の結果として、アプリケーションのコードベースは普遍的である — サーバーとクライアント 共通 で1度だけコードを書く
  3. クライアントサイドのアプリは、サーバーでレンダリングされた HTML を再レンダリングすると言うより既存の要素にイベントリスナ(などなど)をアタッチして ハイドレート する
  4. 瞬時に次のページに移動できる
  5. オフラインや、その他のプログレッシブウェブアプリの特徴をすぐにサポートしている
  6. 最初のページに必要な JavaScript と CSS のみが最初に読み込まれる。 つまり、フレームワークはルートレベルで自動的にコード分割を行い、より細かい手動制御のために動的な import(...) をサポートしている
  7. パフォーマンスに妥協がない
  8. ホットモジュールリローディングやその他痒い所に手が届く様々な機能など、最高の開発環境を提供している
  9. 出来上がったコードベースは、理解しやすくメンテナンス性が高い
  10. システムのあらゆる側面を理解し、カスタマイズをすることができる — webpack の設定をフレームワークの中に閉じ込めず、できるだけ隠された「配管」を少なくしている
  11. フレームワーク全体を1時間以内に学習することは、経験豊富な開発者でなくても簡単にできる

Next.js はこの理想に近いです。 もし Next.js に出会ったことがないなら、 learnnextjs.com でチュートリアルを見てみることを強くおすすめします。 Next は素晴らしいアイディアをご紹介します: あなたのアプリケーションの全ページは your-project/pages ディレクトリにあるファイルで、それらのファイルのそれぞれが React のコンポーネントです。

それ以外のことについては、この画期的な設計の方針に基づいています。「コンポーネント名推測ゲーム」をして遊ぶわけではなくファイルシステムを見るだけでいいので、特定のページを受け持つコードを見つけるのは簡単です。 プロジェクトの構造のことで無駄に細かいことを考える必要がある時代は終わりました。 また、 SSR(サーバーサイドレンダリング)とコード分割の組み合わせは (React Router チームが「サーバーでレンダリングされ、コード分割されたアプリに挑戦する人たちに神の祝福あれ」と言って断念したものではありますが) 些細なことです。

それでも、完璧なわけではありません。 とても素晴らしいもの の粗探しをするのは無粋かもしれませんが、いくつか挙げられます:

  • Next は「ルートマスキング」と呼ばれる機能を使ってイケてる URL を生成します (例えば /post?slug=hello-world の代わりに /blog/hello-world)。 これは、アプリケーションの構造に対するディレクトリ構造の保証を損なうものであり、2つの構造の形式の間で変換する設定を維持する必要があります。
  • 全てのルーティングは普遍的な「ページ」であると想定されます。 しかしながら、 301 redirect やページにデータを提供する API エンドポイントのような、サーバーでのみレンダリングされるルーティングが必要になることは一般的にあることで、 Next はこれに関して良い解決策を持っていません。 こういったケースを処理するために server.js ファイルにロジックを追加することはできますが、これはページの宣言的なアプローチに相反するものです。
  • クライアントサイドのルーティングを利用する場合、リンクに標準的な <a> タグを使うことができません。 代わりに、例えば本ブログ記事のようなマークダウンコンテンツでは利用不可能な、フレームワーク固有の <Link> コンポーネントを使わなければいけません

しかしながら、実際の問題として、これらのような良い機能には代償が伴います。 もっともシンプルな Next のアプリケーションは、 — 静的なテキストを表示する「hello world」ページですが — 66kb の 圧縮された JavaScript を含みます。 解凍すると 204kb になりますが、これは、パフォーマンスがユーザーの定着率を左右する重要な要素であるときに、モバイルデバイスが一度に解析するには大きく問題のあるコードの量です。 そして、これが ベースライン になります。

私たちなら、もっとより良くできます!

フレームワークパラダイムシフトとしてのコンパイラ

Svelte は根本的な思想を紹介しています: UIフレークワークがフレームワークではなく、コンポーネントをスタンドアロンな JavaScript モジュールに変換するコンパイラだったらどうですか?React や Vue のような、アプリのことを何も知らず無難なソリューションにならざるを得ないライブラリを使う代わりに、私たちは高度に最適化された純粋な JavaScript を送り出すことができます。 アプリケーションに必要なコードだけで、仮想DOMをベースとしたソリューションのようなメモリやパフォーマンスのオーバーヘッドはありません。

JavaScript の世界は このモデルに向かっていますStencil は Ionic のチームによる Svelte からインスピレーションを受けたフレームワークで、 Web コンポーネントにコンパイルされます。 Glimmer は スタンドアロンな JavaScript にコンパイル するわけではありませんが (その長所と短所については、別のブログの記事にしたいと思います)、このチームはテンプレートをバイトコードにコンパイルすることについての興味深い研究を行なっています。 (React は このような動きに足を踏み入れていますが、彼らの現在の研究は JSX で書かれたアプリケーションのコードを最適化することに焦点を当てており、これは間違いなく Angular、 Ractive、 そして Vue がここ数年で行なっている事前最適化によく似たものです)

私たちがこの新しいモデルを出発点として用いるとどうなるのでしょうか?

Sapper の紹介

Sapper はその問いへの答えです。 Sapper は Next.js スタイルのフレームワークで、この記事の上部にある11の基準を満たしつつ、ブラウザに送信されるコードの量を劇的に減らすことを目的としています。 Express と互換性のあるミドルウェアとして実装されているため、理解しやすく、カスタマイズも容易に行えます。

同じ「hello world」のアプリケーションでも、React や Next では 204kb もあったものが、 Sapper ではたったの 7kb です。 クライアントサイドのルーティングを処理する Sapper のごく僅かなランタイムを除いて、インタラクティブではないページでは 一切の JavaScript を読み込まないなどの最適化の可能性を探っていくうちに、将来的にこの数字はさらに小さくなっていくでしょう。

さらに、「RealWorld」の例はどうでしょうか? 好都合なことに、 Medium クローンの実装をフレームワークで開発するという RealWorld プロジェクトでは、その結果を知ることができます。 Sapper での実装 では、インタラクティブなホームページを表示するのに 39.6kb (zip形式では 11.8kb) かかります。

アプリ全体のコストは 132.7kb (zip形式で 39.9kb) で、リファレンスの React/Redux での実装の 327kb (85.7kb) よりは著しく小さいですが、例え同じくらいの大きさであってもコード分割のおかげで 体感的には 速くなっています。そしてこれが重要なポイントです。 アプリケーションではコード分割をする必要があると言われていますが、 React や Vue のような伝統的なフレームワークを利用している場合、最初にコードを分割するチャンクのサイズには厳しい下限があります — フレームワーク自体がアプリ全体のサイズの中で大部分を占めていると考えられるからです。 Svelte のアプローチは、もはやそのようなことはありません。

しかしサイズの話は物語の一部に過ぎません。 また、 Svelte アプリケーションはパフォーマンスやメモリ効率が非常に高く、「ミニマル」や「シンプル」なUIライブラリを選択した場合に犠牲にしてしまうような強力な機能がこのフレームワークには含まれています。

トレードオフ

多くの開発者が Sapper を評価する際の最大の難点は、「それでも React は好きだし、使い方もすでに知ってるよ」ということでしょうが、それは妥当なことです。

そんな方々には、ぜひ代替フレームワークを試していただきたいと思います。 嬉しい驚きがあるかも知れません! Sapper による RealWorld の実装ではソースコードの総行数が 1,201 行なのに対し、リファレンスでの実装では 2,377 行となっています。 これは、Svelte のテンプレート構文を使って概念を非常に簡潔に表現できるからで、5分もあればマスターできます。 使われていないスタイルの削除と縮小化が組み込まれたscoped CSSを取得し、必要に応じて LESS のようなプリプロセッサを使用することができます。 Babel を使用する必要はありません。 SSR は、文字列を連結するだけなのでとてつもなく高速です。 また、最近導入した svelte/storeは、コンポーネント階層間で状態を同期させる小さなグローバルストアです。 最悪の場合、自分の正当性が証明されたような気になってしまうかも知れませんね!

とは言え、トレードオフもあります。 いかなる「テンプレート言語」にも病的なまでに嫌悪感を抱く人がいますが、もしかしたらあなたも当てはまるかも知れません。 JSX の支持者は「それはただの JavaScript だ」と酷評するでしょうが、そこには React の最大の強みである、無限の柔軟性があるのです。 そのような柔軟性にはトレードオフがつきものですが、ここでそれについて議論するつもりはありません。

そして、 エコシステム があります。 特に React を取り巻く世界では、開発ツール、統合開発環境、付属ライブラリ、チュートリアル、 StackOverflow の回答、就職の機会に至るまで、他の追随を許さないものとなっています。 確かに、ツールを選ぶ主な理由として「エコシステム」を挙げるのは、無難な位置にとどまってしまっていて進歩の波に取り残されがちであることを示していますが、それでも既存の企業にとっては大きなポイントになります。

ロードマップ

まだバージョン 1.0.0 ではありませんし、そこに辿り着くまでにいくつか修正する点があるかも知れません。 それが実現した暁には (もうすぐです!)、ワクワクするようなたくさんの可能性を秘めているでしょう。

私は、 Web パフォーマンスにおける次の新天地は「アプリケーション全体の最適化」だと考えています。 現在、 Svelte のコンパイラはコンポーネントレベルで動作していますが、コンポーネント間 の境界を解釈することができるコンパイラがあれば、より効率的なコードを生成することができます。 React チームの Prepack の研究 も同様の考え方に基づいていますし、 Glimmer チームもこの分野で興味深い研究を行なっています。 Svelte と Sapper はこれらのアイディアを活かすことのできるポジションにいます。

Glimmerといえば、コンポーネントをバイトコードにコンパイルするというアイディアは、2018年にはおそらく盗むことになるでしょう。 Sapper のようなフレームワークは、アプリの特性に応じてどのコンパイルモードを使うかを決めることができます 。 初回のルーティングでは JavaScript を提供して起動時間を最短にし、その後のルーティングではバイトコードインタプリタを遅延なく提供することで、起動サイズとアプリ全体のサイズの最適な組み合わせを実現することもできます。

とは言え、 Sapper の方向性の大部分はユーザーに決めてもらいたいと考えています。あなたが最先端の生活を楽しみ、 Web アプリケーションの構築方法の未来を形作る手助けをしたいと思っている開発者であれば、ぜひ私たちの GitHubDiscord に参加してください。