メインコンテンツへスキップ

Sapper: 理想的なウェブアプリフレームワークに向けて

次の一歩を踏み出す

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

もし完璧なNode.jsウェブアプリケーションフレームワークの特徴を挙げる必要があるとしたら、おそらく以下のようなものになるでしょう。

  1. 高速な初期ロードとSEOに関する注意事項がないように、サーバーサイドレンダリングを行うべきである。
  2. 当然のこととして、アプリのコードベースはユニバーサルであるべきです — サーバーとクライアントの両方に対して一度書くだけで済むべきです。
  3. クライアントサイドのアプリは、サーバーでレンダリングされたHTMLをハイドレートし、既存の要素を再レンダリングするのではなく、イベントリスナーなどをアタッチするべきです。
  4. 後続のページへのナビゲーションは瞬時に行うべきです。
  5. オフライン、およびその他のプログレッシブウェブアプリの特徴は、すぐにサポートされるべきです。
  6. 最初のページに必要なJavaScriptとCSSのみが最初にロードされるべきです。つまり、フレームワークはルートレベルで自動コード分割を行い、より詳細な手動制御のために動的なimport(...)をサポートする必要があるでしょう。
  7. パフォーマンスに妥協は許されません。
  8. ホットモジュールリローディングやすべての装飾を備えた、一流の開発者体験。
  9. 結果として得られるコードベースは、理解しやすく保守しやすいものでなければなりません。
  10. システムのあらゆる側面を理解し、カスタマイズできるようにする必要があります — フレームワークにロックされたwebpackの設定や、隠された「配管」はできるだけ少なくする必要があります。
  11. 経験豊富な開発者だけでなく、1時間以内にフレームワーク全体を簡単に学ぶことができるはずです。

Next.jsはこの理想に近いものです。まだ触れたことがない場合は、learnnextjs.comでチュートリアルを学ぶことを強くお勧めします。Nextは素晴らしいアイデアを導入しました。それは、アプリのすべてのページがyour-project/pagesディレクトリ内のファイルであり、それらの各ファイルが単なるReactコンポーネントであるということです。

その他すべてが、その画期的な設計決定から生まれます。特定のページを担当するコードを見つけるのは簡単です。なぜなら、「コンポーネント名を推測する」のではなく、ファイルシステムを見るだけで済むからです。プロジェクト構造に関する議論は過去のものです。そして、SSR(サーバーサイドレンダリング)とコード分割の組み合わせ — React Routerチームが諦めて、「サーバーレンダリングされたコード分割アプリに挑戦する人々に幸運を」と宣言した — は非常に簡単です。

しかし、完璧ではありません。非常に優れているものに欠点を挙げるのは意地悪かもしれませんが、いくつかあります。

  • Nextは、「ルートマスキング」というものを使用して、きれいなURLを作成します(例:/post?slug=hello-worldの代わりに/blog/hello-world)。これは、ディレクトリ構造がアプリ構造に対応するという保証を損ない、2つの形式を変換する設定を維持することを強制します。
  • すべてのルートがユニバーサルな「ページ」であると想定されています。しかし、301リダイレクトや、ページ用のデータを提供するAPIエンドポイントなど、サーバーでのみレンダリングする必要があるルートが必要になるのはごく普通のことです。Nextにはこれに対する優れたソリューションがありません。これらのケースを処理するためにserver.jsファイルにロジックを追加できますが、ページに対して採用された宣言的なアプローチとは異質な感じがします。
  • クライアントサイドのルーターを使用するには、リンクは標準の<a>タグにできません。代わりに、フレームワーク固有の<Link>コンポーネントを使用する必要があります。これは、たとえばこのブログ記事のようなマークダウンコンテンツでは不可能です。

しかし、本当の問題は、そのすべての利点が代償を伴うということです。最も単純なNextアプリ — 静的なテキストをレンダリングする単一の「hello world」ページ — は、66kbのgzip圧縮されたJavaScriptを必要とします。解凍すると、204kbになり、パフォーマンスがユーザーが定着するかどうかを決定する重要な要素である場合、モバイルデバイスが解析するためのコードとしては非常に大きな量になります。そして、それはベースラインです。

もっとうまくやれるはずです!

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

Svelteは画期的なアイデアを導入しました。UIフレームワークがフレームワークではなく、コンポーネントをスタンドアロンのJavaScriptモジュールに変換するコンパイラであったらどうでしょうか?アプリについて何も知らず、したがってすべてのニーズに対応できるソリューションである必要があるReactやVueのようなライブラリを使用する代わりに、高度に最適化されたバニラJavaScriptを配信できます。アプリに必要なコードのみで、仮想DOMに基づくソリューションのメモリとパフォーマンスのオーバーヘッドなしで。

JavaScriptの世界はこのモデルに向かっています。IonicチームのSvelteに触発されたフレームワークであるStencilは、Webコンポーネントにコンパイルします。GlimmerはスタンドアロンのJavaScriptにはコンパイルしませんが(賛否両論は別のブログ記事に値します)、テンプレートをバイトコードにコンパイルすることについて興味深い研究を行っています。(Reactはこのアクションに参加していますが、現在の研究はJSXアプリのコードを最適化することに焦点を当てており、これはおそらくAngular、Ractive、Vueが数年前から行っていた事前最適化に似ています。)

新しいモデルを開始点として使用したらどうなるでしょうか?

Sapperの紹介

Sapperはその質問に対する答えです。Sapperは、この記事の冒頭にある11個の基準を満たし、ブラウザに送信されるコード量を大幅に削減することを目的としたNext.jsスタイルのフレームワークです。 Express互換のミドルウェアとして実装されているため、理解とカスタマイズが簡単です。

ReactとNextで204kbを必要とした同じ「hello world」アプリは、Sapperではわずか7kbです。クライアントサイドルーティングを処理する小さなSapperランタイムを除いて、インタラクティブでないページに対してJavaScriptをまったく送信しないなど、最適化の可能性を探求するにつれて、その数は将来さらに減少する可能性があります。

より「現実世界」の例はどうでしょうか?都合の良いことに、Mediumクローン実装の開発にフレームワークが挑戦するRealWorldプロジェクトは、それを見つける方法を提供してくれます。Sapper実装では、インタラクティブなホームページをレンダリングするのに39.6kb(圧縮時11.8kb)かかります。

アプリ全体のサイズは132.7kb(圧縮時39.9kb)で、React/Reduxの参照実装の327kb(圧縮時85.7kb)よりも大幅に小さいですが、たとえ同じくらい大きくても、コード分割のおかげでより速く感じられるでしょう。そして、それは重要なポイントです。アプリをコード分割する必要があると言われていますが、アプリがReactやVueのような従来のフレームワークを使用している場合、最初のコード分割チャンクのサイズにはハードな下限があります。それはフレームワーク自体であり、アプリ全体のサイズのかなりの部分を占める可能性があります。Svelteのアプローチでは、もはやそうではありません。

しかし、サイズは物語の一部にすぎません。Svelteアプリは非常に高性能でメモリ効率も優れており、フレームワークには、「最小限」または「単純」なUIライブラリを選択した場合に犠牲にする強力な機能が含まれています。

トレードオフ

Sapperを評価する多くの開発者にとって最大の欠点は、「Reactが好きで、すでに使い方がわかっている」ということでしょう。それは当然のことです。

あなたがその立場にいるなら、少なくとも代替フレームワークを試してみることをお勧めします。嬉しい驚きがあるかもしれません!Sapper RealWorld実装は、参照実装の2,377行に対して、合計1,201行のソースコードで構成されています。これは、Svelteのテンプレート構文を使用すると、コンセプトを非常に簡潔に表現できるためです(習得するのに5分もかかりません)。未使用のスタイル削除と組み込みのminify機能を備えたスコープCSSを取得し、必要に応じてLESSなどのプリプロセッサを使用できます。Babelを使用する必要はなくなりました。SSRは単なる文字列連結なので、驚くほど高速です。そして、最近svelte/storeを導入しました。これは、ボイラープレートなしでコンポーネント階層間で状態を同期する小さなグローバルストアです。最悪の場合でも、自分の正しさを確信することになるでしょう!

しかし、それにもかかわらず、トレードオフはあります。一部の人はあらゆる形の「テンプレート言語」に病的な嫌悪感を持っており、それはあなたにも当てはまるかもしれません。JSX支持者は、「それは単なるJavaScriptだ」というマントラであなたを打ちのめすでしょう。そして、そこにReactの最大の強さがあります。それは無限に柔軟であるということです。その柔軟性には独自のトレードオフが伴いますが、ここではそれについて議論するつもりはありません。

そして、エコシステムがあります。特にReactを取り巻く世界 — 開発ツール、エディター統合、補助ライブラリ、チュートリアル、StackOverflowの回答、仕事の機会まで — は比類のないものです。「エコシステム」をツールを選択する主な理由として挙げることは、進歩の波に打ちのめされる可能性のある、ローカルな最大値にとらわれていることの兆候であることは事実ですが、それでもなお、既存のツールを支持する上で大きなポイントです。

ロードマップ

まだバージョン1.0.0には到達しておらず、そこに至るまでにいくつかの変更があるかもしれません。一度到達すると(すぐに!)、多くのエキサイティングな可能性が開かれます。

私は、ウェブパフォーマンスの次のフロンティアは「アプリ全体の最適化」だと考えています。現在、Svelteのコンパイラはコンポーネントレベルで動作していますが、コンポーネント間の境界を理解するコンパイラであれば、さらに効率的なコードを生成できる可能性があります。ReactチームのPrepackの研究も同様の考えに基づいており、Glimmerチームもこの分野で興味深い研究をしています。SvelteとSapperは、これらのアイデアを活用するのに有利な立場にあります。

Glimmerの話が出ましたが、コンポーネントをバイトコードにコンパイルするというアイデアは、2018年に私たちが盗む(採用する)可能性が高いものです。Sapperのようなフレームワークは、アプリの特性に基づいてどのコンパイルモードを使用するかを決定できるはずです。最初のルートには可能な限り高速な起動時間のためにJavaScriptを提供し、後続のルートにはバイトコードインタープリターを遅延ロードすることで、起動時のサイズとアプリ全体のサイズの最適な組み合わせを実現することもできます。

しかし、主に、Sapperの方向性はユーザーによって決定されるようにしたいと考えています。あなたが最先端の環境で開発を楽しむタイプの開発者で、Webアプリの構築方法の未来を形作るのに協力したい場合は、GitHubDiscordにご参加ください。