フロントエンドとSPA職人の目指したものの歴史と概略
年末年始にフロントエンド論みたいな記事をいくつか見たが、僕ら古のSPA職人がやってきたフロントエンドという職域と目指していたものが失伝しかけている気がするので、ここに時代ごとに何を考えていたか、雑に書き殴る。
注意点として、 2004から始まるが、自分がプログラミングを始めたのが2010, 業務としてコードを書き始めたのが 2012 なので、解像度が高いのはそれ以降になる。
tl;dr
- 2004: 動き出す HTML
- 2011: 構造化のはじまり
- 2015: 贅沢品としてのSPAとコミュニティ分化
- 2017: 貧者のSPA
- 2019: 守破離としてのパフォーマンス
2004: 動きだす HTML
AJAX の時代。要は XMLHTTPRequest で取得したコンテンツに応じて、動的書き換えをDOM書き換えを行うこと。今では名付けるほどでもない操作だが、HTMLが静的なものをやめたことは、それなりに大きな衝撃だった。
まだJS側から URLを動的に書き換える history.push
がないので、あまりに書き換えすぎるとセマンティクスや検索性を損ねてしまい、Canvas ゲームなどを特定のケースを除いて簡易な書き換えに限定しましょう、という空気感があった気がする。
jQuery 以前はコーディングスタイルもクソもなく、各自が自分が好きな言語(母言語)の書き方を勝手に適応していた(のが古い StackoverFlow を見ることでわかるだろう)。
2008以降の jQuery 流行以降にフォーマッタの登場等、多少の知性の目覚めみたいなものが見えるが、雑に書く言語なのは変わらず。
JS は真面目に運用できないので、多少真面目なやつは ActionScript (Flash用のES4)で書くという風潮があった。ActionScript はだいたい Java っぽく運用されていた。 IDE も Eclipse ベースだったし...
だいたい TypeScript だった ES4 は野心的すぎて、当時のオピニオンリーダーだったダグラス・クロックフォード(JSONの発見/発明者)らによって強く反対され、2008年ぐらいに頓挫した。2009ぐらいにはとりあえず現状の安定版として ES5 が定義された。
この ES5 がなかなか退場しない IE8/11 で動く水準だったので、長らく各種ツールの吐き出すビルド水準として存在し続けることになる。
この時代のオーパーツとして、 Java から JS をコンパイルする GWT や、かなりきつい縛りを導入してminify効率を上げる Google Closure Compiler がある。
2011: 贅沢品としてのSPA
主にジョブズのせいで Flash が死ぬことになったので、Flash にあった要望が JS とフロントエンドに集中しはじめた。
複雑な画面の実装を、MVCアーキテクチャ化した jQuery とでも言える Backbone によって実装が試みられ、あまりの実現難度と管理コストから、SPAは贅沢品扱いされた。
Node.js の流行から、主に Ruby > Node.js > フロントエンドという経路で rubygems のようなまともなパッケージシステム等のツールチェインを導入しようという試みが始まる。
フロントエンドのライブラリを npm / bower にアップロードする試みが始まる。 ブラウザでも Node でも動く UMD という形式が一部で広まる。
大雑把にこんなの
var MyMod;...
if (typeof module === "object") {
module.exports = MyMod
} else {
window.MyMod = MyMod;
}
Node.js のパッケージシステムをエミュレートする browserify が生まれ, それを精神的に引き継いだ webpack 等のバンドラに繋がっていくことになる。
需要に対しての使われ方に反し、 ES5 は言語仕様的な限界を迎えていた。 結果、AltJS と呼ばれる JS を生成するトランスパイラが流行る。有名なのは CoffeeScript と TypeScript で、CoffeeSrcipt は Rails のフロントエンド機構に組み込まれる等して一定の地位を築いた。
TypeScript は型検査の厳密さよりも段階的に既存コードに型をつけていける漸進的型付けという仕組みを採用しており、2012年の初期にこれが仕込まれたことが、エコシステムを段階的に型化していくインフラを整えて、この型定義の蓄積が今現在の覇権の伏線になっている。
2015~
ES2015 でモジュールシステムが定義されるが、 それをサポートしない IE11 が生き残っていたこと、ES2015を使ってもリソース解決が多段になるRTT問題が解決しない等の問題が残ったので、結局 webpack 等のバンドラーを使って最適化していくことが既定路線になった。
自分の知る限り、2016年ほどから React が流行りだした。同時期に流行っていたVue等のフロントエンドライブラリはすぐに 仮想DOM を真似していったので、結果としてすぐにほとんどが仮想DOMベースの宣言的アーキテクチャという状態になった。
贅沢品だった SPA がこのへんで一般的な開発者に届くようになったように思う。
SPA 技術の普及と同時に、開発コミュニティの乖離が顕著になる。SPA開発のツールチェインは既存の Web 開発の文脈になく、 Node.js 系ツールチェインの上に構築されていた。Node.js 系開発者は当然JSに強く、既存のWeb開発者はどちらかというと HTML/CSS のマークアップ技術に強い。
マークアップエンジニアは所属する会社のサーバーサイドMVCフレームワークのビュー部分のテンプレートエンジンを使うことが多く、またサーバーサイドフレームワークの選定は、技術というより、その会社のアイデンティティの問題が多く絡んでいた。
この時期のフロントエンドエンド技術者というかSPA職人は、本音では既存のMVCは時代遅れだと思いつつ、MVCから部分的にSPAを切り出す作業ばかりやる羽目になっていたと思う。
また、この時期に富豪的なSPAがユーザーのヘイトを買うようになっていた。SPA本来、初期化時間を多めにとって動的要素を増やし、ユーザーの操作に即応できるようなるための技術だが、部分的なSPA採用でルーティングが中途半端だったりで、ロード時間の増加というデメリット部分だけが目立つようになった結果。
2017: 貧者のSPA
フロントエンドにおいて、とくにSEOを意識してサーバーにテンプレートエンジンを置いてクライアントでリタッチするのはもはや限界、というのは 2012 からわかっていた。コンテンツの生成をクライアントと同じ JavaScript, つまり Node.js が担わないといけないことから、サーバーサイドに部分的に進出するのは必然だった。 それが Next/Nuxt。
Nextはルーティングとルーティングごとの入力props、それらがクライアントで決定されるか、サーバーで決定されるか、をAPIとして意味論的に落とし込んだことが、大きな発明だったと思う。
// pages/foo/[id].tsx => /foo/*
export async function getServerSideProps(ctx) {
return {
props: {/*...*/}
}
}
export default function Foo() {/*...*/}
コードの書く文化自体もフレームワークに合わせて変わってきた。React/Next の入出力の冪等性というテーマ、JSが元々入出力を担っていること、Promise という実質的なモナドによるデータの分離、Rust や Go の脱クラス志向の影響を受けて、自由度が高い表現力をもつTypeScriptで、関数型っぽく書くのが好まれるようになった。
そもそも、モバイル対応の名目で、サーバーサイドフレームワークでもAPIとビューが切り出されるのが当然になりはじめていたので、WebだがAPIの利用者、という立ち位置でSPAを切り出して作れるようになる土壌もあった。これが Jamstack に繋がる。
2019: 守破離としてのパフォーマンス
SPAが簡単に作れるようになった反面、その功罪もはっきり目に見えるようになってきた。
古のフロントエンドとSPAの実装が高難度だった分、新しい技術は主に開発者の生産性にフォーカスされ続けてきた。その結果、今なら誰でもSPA(もどき)は作れるようになり、SPAを作る技術は完全にコモディティ化した。
それと同時に、コモディティ化の結果としての開発者の平均レベルの低下が大きな問題になった。我々がNode.js以来追い求めていたサーバーサイドと同じようにライブラリを組み込める基盤による、富豪的で無節操なライブラリ追加問題だ。
もう何度貼ってるかわからないけど、開発者の生産性を掲げ続けた結果、フロントエンドが不要なJSまみれになり、ユーザーに対する裏切りになったという Alex Russell の指摘は何度も読んでほしい。
The "Developer Experience" Bait-and-Switch - Infrequently Noted
(ここは強く言っておきたいが、これはSPAに限った話ではない。下手なウェブサイトが雑に読み込む各種JS郡の方が、SPAを構成するフレームワークよりオーバーヘッドは大きいことはよくある)
SPA肥大化の問題はどこでも起きていて、結果モノリシックなSPAを解体して、部分的にロードするコンパイル時最適化や、ハイドレーション技術が注目されるようになっている。React のコア部分を小さく実装する preact はだいぶ前からあったが、コンパイル時に最適化を済ませてしまう svelte, その合わせ技の solid や quik 等が注目されている。
それと同時に、SPAによる動的ページもオーバーヘッドが少なく同じような機能が作れるなら、もはやSPAである必要がなくなった。結果、SPA/MPAの棲み分けというテーマが生まれた。巨大JSインスタンスを引き回すから再初期化ができない、つまりシングルページである必要があったわけで、遅延ロードが極まって読み込みが常に最適化されるなら、もはや多機能なページでもSPAでこだわる必要はないのかもしれない。
マイクロフロントエンドの試みがあるが、本来なら分離の核の仕様技術たる WebComponents があまりにも貧弱すぎてコミュニティが振り回されているのが現状。悲しい。
コンパイル時処理が複雑化した結果、CIのビルドを最適化し続ける FrontendOps の需要が高まり続けている。自分も 2022年はブラウザみてる時間より、シェルスクリプト書いたりGitHub Actions みてる時間のほうが長かった。
というわけで、複雑化したフロントエンドは守破離の時代を迎えていて、パフォーマンス(とここでは書かなかったがセキュリティ)を意識ができることが一流のフロントエンドの条件となった、というのが自分の認識。
まとめ
今フロントエンドに残っている人は、大雑把に次のように分類できると思う。
- 古典派: ES5時代に残った人たち
- テンプレートエンジン派: 宣言的UIを使いつつ、しかし単に新しい記法として需要した人たち
- 富豪的SPA派: Next/Nuxt を既存のサーバーサイドのように使う人たち
- FrontendOps: CI上でパフォーマンス上の問題を発見して尻拭いする人達
スクールの隆盛等でフロントエンドを名乗る人は増えたが、結果として増えたのはテンプレートエンジン派と富豪的SPA派だと思っていて、フロントエンドが足りない、の真意はアーキテクチャやパフォーマンスを破綻させずに書ける人が足りない、という話だと思う。
古の Rails エンジニアが k8s 等をやるインフラエンジニアになりがちなように、昔ながらの フロントエンドやってた人は Frontend Ops をしている、というのが自分の観測範囲の話。
今後はハイドレーションの進化でさらにパーシャルロードが進んだり、そもそも Rust 書くようになったり、AIによるコード生成がテンプレートエンジン派の仕事を代替していくと思うが、それらについては気が乗ったら書く。
おわり
Discussion