💭

jQuery フル活用のレガシーフロントエンドからの脱却に思いを馳せてみた

2020/12/23に公開

この記事は ニフティグループ Advent Calendar 2020 の10日目の記事です。

……書いているのは23日です。すみません。

この記事について

これまで私が担当してきたWebフロントエンドは、普通のCSSと、jQueryを駆使したAjaxをふんだんに使う、いわゆる一昔前に流行った技術スタックによるサービスが多くありました。
その構成を2020年も終わろうとしている現在まで維持している理由は様々ですが、このままずるずると同じ構成で本格的なWebアプリケーションを制作するのは、そろそろ限界が見えてきたようにも思います。

フロントエンドをまるっと載せ替えることを検討した時、昨今話題に上がることが多い React (Next.js) や Vue (Nuxt.js) を使った構成がまず思いつくと思います。
これらを使った場合に考えられる全体の構成や課題について考えてみたポエムがこの記事です。

おことわり

私は前述の通り、今回紹介するフレームワークについて、本格的に活用したことがない中で調べながら記載しています。
もし誤り等ありましたらお知らせいただけますと幸いです。

この記事は、社内勉強会のために作成した資料を一部リライトしたものです。

jQuery を使った Web アプリケーションの課題点

jQueryをはじめとする従来のJSライブラリは、DOMに情報を持たせることを前提としていました。
Ajaxで取得したHTMLやJSONを加工して直接ページを書き換えたり、
また処理に使う値をHTMLの属性値・入力値からその都度引っ張ってきていました。

そのため、毎回DOMへのアクセスが発生したり、余計な部分までJSによる再レンダリングを行ったりと、
パフォーマンス面で問題が出ることがありました。
DOMに依存しているためテストが書きづらいという側面もあります。

またjQueryに関してはほかにも、

  • ブラウザによるJSの実行結果の差が標準化により改善したため、差分を吸収する役割だったライブラリの需要が減った
  • (jQueryに関して)ES201x系に代表されるJSの進化により、jQueryを使わなくても良いケースが増え、単純な処理ならわざわざjQueryを使わなくても可読性を維持して処理が書けるようになった

などの理由により、使われなくなるケースが増えてきていると思います。
(それでも、慣れていると手癖で使えてしまうので、個人でサービスを作るときにはつい手を伸ばしてしまうことも多いのですが……)

React/Next.js をはじめとする最近のJSライブラリ・フレームワークの特徴

仮想DOM

仮想DOMとは、その名の通りDOMに対応する(けどDOMではない)オブジェクトです。
React以降で急速に広まったこの仕組みは、表示に変更を加える際にまず仮想メモリ上で差分を計算し、その部分のみを再レンダリングすることにより、実際のDOMに状態を保持する従来の方式に比べてよりパフォーマンスに優れているという特徴があります。

TypeScript

TypeScriptが当たり前のように使われるようになりました。
型があると安心しますし、真面目にDocコメントを入れることで VSCode などのエディタで補完等の恩恵を受けることもできます。

ただ、今までのJSを書き慣れた人からすると、覚えることが増えてしまうのも事実です。
ただこのあたりは、今後フロントエンドで食っていくには必須の技術だと思って割り切るしかないのかなと思い始めています。

SPA

History APIなどの普及により、JavaScriptで画面遷移したように見せつつURLも変えることが可能になりました。
React や Vue.js などはそれぞれに対応した Router ライブラリが存在するため、
ルーティング処理を簡単に書くことができ、HTMLの再読み込みを挟まない高速な画面遷移を実現できます。

SPA と Next.js と Nuxt.js のレンダリング

Next.js は、 React で Server Side Rendering (SSR) を簡単に実装するために生まれました。
Vue.js にもこれにインスパイアされた Nuxt.js がありますが、そもそもこれらはなぜ生まれたのでしょうか。
また、それぞれのフレームワークのレンダリングについても簡単に触れたいと思います。

従来のSPA (Client Side Rendering = CSR)

軽量な HTML ファイル + アプリケーションが書かれた app.js の組み合わせです。
(キャッシュを効かせるなどの理由で JS ファイルが分割されることも多いですね)

以前はGoogle BotがJSを解釈できないためSEO上不利な状況が続いていましたが、
最近はきちんと解釈してくれるようですので、その点での懸念はかなり軽減されたようです。

ただし、アプリケーションが巨大になるほど初回レンダリングまでに時間がかかりがちになってしまうため、
純粋にパフォーマンスの観点でSEO的にはマイナスになりやすいと言えると思います。
また、Twitter BotなどはJSが解釈できないため、SNSでの展開(OGPの設定などをJSで行う場合)が難しくなります。

Server Side Rendering (SSR)

サーバーサイドレンダリングは、Node.jsサーバーでページをレンダリングして、その結果を返却する形式です。
SPAはHTMLとJSが配信できればサーバーは不要(S3だけでも動く)でしたが、この場合はサーバーが必要です。
また、SSRの速度を出すためには、それなりのサーバースペックが必要になります。

多くの場合、ランディングページだけSSRで返却し、それ以降は CSR に移行することが多いと思います。

Static Site Generation (Next.js)

Next.js における Static Site Generation は、SSRと同様にサーバーが必要です。
サーバー上でアプリケーションをビルドする際、各ページで行われている処理の内容を確認し、
完全に静的なHTMLとしてその場で解決できる場合は静的なファイルとして出力してしまい、配信時のレンダリングをスキップするという形式です。
具体的には getStaticProps に指定してある内容はビルド時に解決可能、 getServerSidePropsgetInitialProps がページにある場合には、ビルド時の解決が不可能と判断し、配信時にはSSRを行います。

Incremental Static Regeneration

さらに、最近 Next.js には Incremental Static Regeneration という形式が追加されました。
これはビルド時の静的ファイル出力を行わず、アクセス時にレンダリングした内容を静的ファイルとして保持するという方法です。
2回目以降のアクセスでは、そのキャッシュを高速に返却しながら、新しい情報でキャッシュを自動でで再生成してくれるため、
アクセスがある限り最新の状態のキャッシュを保ち続けることが可能です。
また、ページを事前にレンダリングしないため、ページ数が多いサイトや、ユーザーコンテンツなどページが無限に生まれるサービスにおいても適用できるようになりました。

SSG Serverless (Next.js)

SSG における SSR 部分を各種 CDN の Worker で動かす仕組みです(Serverless Framework)。
AWSであれば Lambda@Edge (CloudFrontで動くLambda) などで動作させます。
例えば、静的ページならそのまま S3 に配置しておいた事前ビルドを返し、動的ページならLambda上でレンダリングして返却します。
レンダリングして返した結果がCDNにキャッシュされるので、2回目以降は高速に返却されます。

公式にサポートされているものではありませんが、SSGの恩恵を Serverless で受けられるのが特徴です。

Static HTML Export (Next.js) / Static (Nuxt.js)

ビルド時にすべてのページを静的なHTMLファイルとして出力する方法です。
サーバーなしでデプロイ可能なため、S3やGitHub Pagesなどさまざまな環境で動かすことができます。

Nuxt.jsにおいて static モードはこのような動作なのですが、Nuxt.jsだと若干意味が変わるので驚きました。

どうやって移行していくか

残念ながら、会社の技術スタック的にも、すぐにすべてのサービスを
これらのフレームワークを使いこなした形に置き換えるのは難しいのが現状だと思っています。

弊社のプロダクトの場合、ページは動的に生成しないといけないレベルの数があり(100万単位)、
事前にビルドするというのは現実的ではないため、
Next.js の Incremental Static Regeneration はきちんと触れておきたい機能だと感じています。

また、内部ツールや一部のページではReactやVueを使っていることもあります。
今後も少しずつ小さいところから採用を増やすことで実績を重ねていきたいと思っています。

参考資料

以下の記事の内容を参考にさせていただきました。ありがとうございます。
Frontend Study #1: 基調講演 - Frontend 領域を再定義する

Discussion