Next.js + SvelteによるnoteのフロントエンドApp分割
※ この記事は2021年6月21日に行われた「進化するフロントエンド2021 − 4プロダクトから学ぶSPA/PWAの技術と実践−」の登壇を文字起こししたものになります
noteのフロントエンドの歴史
ではまず自己紹介をさせていただきます。フロントエンドエンジニアの山形と申します。よろしくお願いします。
去年の3月に入社したので、noteには1年3ヶ月くらいいます。趣味でコーヒーの焙煎とかやっています。
今回はnoteが抱えているフロントエンドの課題をどう解決していったのかを話していきたいと思います。
最初にnoteが今までどうやって実装されていたのか、その歴史を説明していきます。
初期のnoteはRuby on Railsのアセット上に構築されたSPAサイトで、フロントはAngularJSとCoffeeScriptで書かれていました。
この構成はかなりの問題を含んでいました。
1つ目はSSRができないことです。まあ、この当時はSSRという言葉すらなかったかもしれませんが、この構成には様々な弊害がありました。Googleのクローラーに対してHTMLを返すことができないので、ヘッドレスブラウザでHTMLを返す必要がありました。SEO対策に苦労します。
2つ目はSprocketsを使ったSPAだと動作が遅くなってしまう点です。Webpackを利用してビルドしたソースを渡しているわけではなく、単純にscriptタグがhtmlに追加される方式などが原因の一つです。
そして最後はCoffeeScriptで書いている点です。CoffeeScriptに縛られていると、モダンなJavaScriptの恩恵を享受できないという問題があります。
それらを解決するために2018年にAngularからNuxt.jsへ移行するプロジェクトがスタートしました。
その成果もあって現在は一部のページをのぞいて、ほぼNuxt.jsに移行が完了しています。役目を終えたNuxtチームはすでに解散しており、僕が入社したときには問題はほとんど解決済みになっていました。
Nuxt.js移行による新たな課題
しかし、Nuxt.jsに移行してもフロントエンドには新たな課題が問題視されるようになってきました。
まず、コード量の問題です。Angular時代にあった機能をすべてNuxt.jsで実装し、新機能もどんどん開発されています。そのため、一つのアプリにのっているコードがかなりの量になってしまっています。
コード量が多くなってしまっているため、比例するようにビルド時間も長くなってしまっています。ローカルにクローンして、ライブラリをインストールして、サーバーを起動して……とデバッグするまでにかなりの時間がかかります。マシンによっては処理落ちしてしまうこともあり、デプロイも長い時間がかかっています。
また、開発による影響範囲が広いという問題も発生しています。どこかで作業するとどこかでバグが発生してしまうという、バタフライエフェクト的な感じで。実際にnoteはこれが原因でサービスの負荷が上がり落ちかけたこともあります。
そして最後にパフォーマンスの問題です。パフォーマンスが低下したことにより、SEOもかなり低下しました。今はパフォーマンス改善チームが別軸であるため、徐々に改善はしています。
Next.jsによるフロントエンドのApp分割プロジェクトがスタート
そういった問題を解決するために、今年の2月ごろからフロントエンドのアプリ分割を始めました。調査自体は以前から行っていたのですが、本格的に作業に取り組み始めたのは今年の2月です。
フロントエンドは今後はNext.jsを使用して開発していく方向にしました。理由としてはReactのTypeScriptサポートレベルが高いことと、Next.jsの進化の速さなどが上げられます。
すでに大きめな機能はNext.jsでの開発で進んでいます。徐々にNuxt.jsの機能を切り離してNext.jsに移行していく予定です。ただ、すぐにNuxt.js捨てるというわけではなく、まだまだ長い付き合いになっていくでしょう。
Next.jsへの移行は共通ルータやスタイルなど様々な壁があるのですが、今回は共通コンポーネントの話をしていきたいと思っています。
共通コンポーネントはSvelteで作成
初めは共通コンポーネントとして、Web Componentsを検討し、Stencil.jsを試しに使ってみました。しかし、Stencil.jsはバンドルサイズが大きく、Web ComponentsはSSRができない問題があったため厳しいという判断になりました
そこから色々と検証し、Svelteがかなりいいんじゃないかという結論になりました。Svelteの良いところはなによりサイズが軽いことです。同じボタンコンポーネントを作ってみると、Stencil.jsは36KBだったのに対し、Svelteはたったの5KBです。しかもこの容量にはReact.jsのアダプターも含まれています。
Svelteはバンドルサイズ以外にもTypeScriptが書きやすいのがかなりの利点です。Vue.jsはそのあたりのサポートが弱くてストレスになりがちでした。SvelteはReact.jsと同じくらい気持ちよく書くことができます。
あとはWebpack configに書けば簡単に動作するのも良いですね。React.jsやVue.jsに組み込んでもすぐ動かすことができます。
Svelteで作った共通コンポーネントはGithub Packagesでライブラリ化をしており、npmでインストールして使えるように分割しています。
Sveltで作ったボタンコンポーネントをReact.jsの中で使用しているサンプルがこちらになります。シンプルに書けているのがわかると思います。
React.jsがマウントしたときに、実際のHTML上にあるDOMをSvelteのターゲットとして与えるだけで動作します。実際にはpropsの変更通知や、イベントのハンドリングは必要ですが、ベーシックなコードであればこれで十分です。
Vueの2系で同じことをするパターンのサンプルがこちらです。
基本的にはインスタンスを持っておいて、マウントして実装してあげれば動作します。
App分割を通してわかったこと
最後に、今回の作業に取り組んでみて個人的に気づいた点を話していきたいと思います。
まず、新しいアプリのフレームワークやライブラリの知識を知っておくのは大事なことだなと改めて実感しました。Svelteは僕ではなく同僚からのおすすめでした。日頃から情報収集をしておくと、いざとなったときに選択肢が広がります。
それからビルドシステムを読み解く力は大切です。ビルドシステムをわかっていないと、どういうタイミングでコンパイルされて、最終的にどんなソースがでてくるのか想像ができません。ビルドの仕組みは学んでおくといいでしょう。
そして実装力も必要になってきます。どんな開発においてもそうですが、言語やフレームワークの習熟度は求められます。SvelteはVue.jsやReact.jsの経験があればある程度はすぐに触ることができます。普段から勉強をして習熟度をあげておくと、新しいライブラリにも柔軟に対応することができます。
最後に分析力です。分析力がないと技術検証をしても、どれが我々に合っているのか判断することができません。ビルドシステムにもかかわってくる話ですが、実際にバンドルされたソースコードを見て、問題解決されたのか分析する力は必要です。
技術的な力も必要ですが、大きな開発を成し遂げるためにはやはり周りの協力は不可欠です。
noteの開発でモヤモヤしたことがあったときに、それらをどうやって解決していくのか一人で考えることがあります。それを仲間に相談するのは大事なことです。口頭でもSlackでも、相談してみると新しいアイディアがでてきたり、自分のアプローチがブラッシュアップされたりしていきます。
それから上長に相談して作業時間をえるのも大事ですね。自分のプライベートな時間を削ってやることではないので。noteはそういったことは自主的にやれる環境なので安心感があります。
noteにはミッション、ビジョン、バリューというものがあり、これに従って行動すれば、自分のためになるようになっています。すばやく試す、つねにリーダーシップを、大きな視点で考えよう。今後もこれらのバリューを指針にして、開発をしていきたいと思っています。
というわけでnoteではフロントエンドエンジニアを募集中です。
noteというサービスが好きな人だとうれしいです。noteはコンテンツが好きな社員が多いので、例えば小説やアニメなどが好きだったり、そうゆう趣味があると仲良くやれるかなと思います。
まずは気軽に応募していただければ。
▼noteを一緒に作りませんか?
▼エンジニアの紹介記事
Text by megaya