🔥

JSConf JP 2023「書いたJavaScriptがそのままブラウザで動く未来へ」スピーカーノート

2023/11/21に公開

この記事は、JSConf JP 2023 で発表した「書いたJavaScriptがそのままブラウザで動く未来へ」のスピーカーノートです。もともと PDF を公開していたのですが、読みにくいという声をいただいたので移植しました。内容はそのままで、見出しだけ付けています。

はじまり

  • こんにちは、今日は「書いたJavaScriptがそのままブラウザで動く未来へ」というタイトルで発表をします。「いやいや、JavaScriptはブラウザで普通に動くだろう」と思われる方もいらっしゃるかもしれませんが、話を聞いてもらえれば何を言っているのかわかると思います。
  • JSConf JPで登壇させていただくのは今回で3度目なのですが、オフラインのカンファレンスで話すのは今回がはじめてなので少々緊張しています。
  • 最初に軽く自己紹介をします。鈴木 颯介と言います。大体のところで Sosuke Suzuki という名前とこのアイコンでやっています。ユビー株式会社でプロダクト開発エンジニアをやりながら、筑波大学でパソコンのお勉強をしています。あと、PrettierというコードフォーマッタのメンテナーとかBabelというJavaScriptのトランスコンパイラのコミッタなどをやっています。
  • また、私の所属しているユビーは今回JSConf JPのプレミアムスポンサーをやらせてもらっています。ブースもありますので興味のある方はぜひお立ち寄りください。
  • ユビーとはあんまり関係ないですが、Babel のステッカーも配布しているのでそちらもよろしければ取っていってください。
  • さて、ここからトークに入っていきます。

「JavaScriptを直接書かない」フレームワークたち

  • 突然ですが、みなさんは仕事でどんなフロントエンド技術を使っていますか?
  • 現在のフロントエンド開発には、数多くの選択肢があります。
  • たとえばReact、Vue、Svelte。そしてそれらのメタフレームワークであるNext、Nuxt、Sveltekit。実際にはもっとたくさんの選択肢があります。Angular、preact、qwick、nueなどもあります。もっと昔のもので言えばBackbornやRiotなどもあります。数えたらキリがないでしょう。
  • ちなみに、私の所属しているユビーのtoCアプリケーションではNext.jsを使っています。
  • もちろん、こういったフレームワークを使わずにJavaScriptをそのまま書いたりjQueryを使っている人もいると思いますし、Ruby on Railsなどのサーバーサイド主体のフレームワークに搭載されているフロントエンドのための機構を使っている人もいると思います。Closure LibraryとかClosure CompilerやClosure Templateなんかを使っている人もいるかもしれませんね。
  • ここでは、私が今名前を挙げた中でも、いわゆる「モダン」なものに注目してみます。
  • 「モダン」というと主観的すぎてイメージがしにくいかもしれないので、なんとなく私が今想像しているものの名前を挙げておきます。Next.jsやNuxt.js、Sveltekit、Astro、Remixあたりでしょうか。
  • もしかしたらここで異議がある方もいるかもしれませんが、この分類を厳密に行うことは今日の発表にとっては本質的ではありませんので、話を続けますね。今は「そういうフレームワーク」について話してるんだと理解してください。
  • これらのフレームワークには共通する特徴があります。それは「JavaScriptを直接書かないこと」です。
  • Next.jsをお使いのみなさん、あなた方のほとんどはTypeScriptとJSXを使っていると思います。
  • もしまだピュアなJavaScriptとhyperscriptを使ってReactコンポーネントを書いているという人がいたら、そのこだわりについてぜひ聞きたいです。
  • Next.jsのプロジェクトを立ち上げて、普通に設定をしていったらTypeScriptとJSXを使うことになると思います。我々普通のプログラマーは、その詳細を把握している必要はありません。便利な世の中になりました。
  • こういったフレームワークが普及する前は、webpackやBabelの設定を一から書いていました。実は私はそれらの設定が人よりちょっとだけ得意でしたが、もうそのときには戻りたくありません。
  • 整理すると、現代を生きる我々の多くは「ブラウザでは実行できない何かしらの言語を使ってソフトウェアを記述し、それをなんらかのツールを使ってブラウザが直接実行できるHTMLやJavaScriptなどの言語に変換している」と言えそうです。
  • なぜそんなことをしているのでしょうか。普段はあまり考えないかもしれませんが、もしもそれらがなくなったときのことを考えれば、理由は明らかだと思います。
  • TypeScriptなしで、JSXなしでReactアプリケーションを書くことを考えてください。これを考えると、TypeScriptの型によって安全なプログラミングが、JSXによって直感的な宣言的UIの記述が可能になっている、ということを思い出せるでしょう。
  • これは逆に考えると、そのままのJavaScriptだけではそれを実現できないから、何かしらの変換を行っていると言えますね。
  • でも待ってください。JavaScriptは高級言語です。それもかなり高級です。私はコンパイラについてそこまで詳しくありませんが、マシンからしたらそれなりに厄介そうですよね。
  • 私は、そんなJavaScriptをコンパイルターゲットとして使っている現状に違和感を感じています。
  • その現状について「悪い」と言うつもりはありません。私は仕事でも趣味でもNext.jsを使っています。でもちょっとだけ違和感があります。
  • 同じような気持ちになったことがある人もいるのではないでしょうか。
  • ブラウザがECMAScript Modulesを実行できるようになってからもwebpackを使い続けて「アレ、おれたちなんでこんなことしてるんだっけ」と思った人はいませんか?私はそのように思いました。
  • そこで、どうしたらJavaScriptをそのままフロントエンド開発に使えるようになるのかを考えてみたいと思います。
  • ここからは、我々がJavaScriptをコンパイルターゲットとして扱っている理由を更に深ぼって、JavaScriptやWebにどのような機能があれば、そのままフロントエンド開発に使えるようになるかを考えます。
  • つまり、ここからが本題です。

JavaScriptのビルドステップ

  • ビルドステップを詳細化することで、我々がJavaScriptをコンパイルターゲットのように扱っている理由を明確にしていきましょう。
  • 最初に注意しておきたいのですが、今から説明する各ビルドステップについて、ここでは便宜上分けて説明しますが、すべての実装においてパスが別れているということではありません。
  • まず最初は、TypeScriptからJavaScriptへの変換です。TypeScriptやMetaのFlowなどの、JavaScriptの構文を拡張する形で型アノテーションを記述する言語を使っている場合、それをそのままJavaScriptエンジンで実行することはできませんので、型アノテーションの部分を除去する必要があります。
  • スライドをご覧ください。このような、ある関数についてその引数が string 型で返り値が void 型であるという注釈を除去します。
  • このステップを行う具体的なソフトウェアは、TypeScriptコンパイラ、Flowコンパイラ、Babel、SWC、esbuildなどです。
  • 次に、JSXからJavaScriptへの変換です。JSXはECMAScriptに含まれている構文ではありません。ECMAScriptの立場から見ると、エコシステムによっていわば勝手に拡張されているということになります。
  • JSXの構文もTypeScriptなどと同じようにJavaScriptエンジンで直接実行できるわけではありませんから、実行できる形に変換する必要があります。
  • JSXの最も一般的な用途は、Reactコンポーネントを記述することだと思います。ですが実際のところJSXをどのようにJavaScriptに変換するかは自由に設定できます。実際にVueなどのReact以外のライブラリでも使うことができます。あまり見かけないですが、ExpressなどのAPIをマッピングすることでサーバーを書くこともできます。
  • つまりこのステップの役割は、事前に決められた設定にしたがって JSX の構文をJavaScriptの関数呼び出しへと変換することなのです。
  • このステップを行う具体的なソフトウェアは、さきほどと同じで、TypeScriptコンパイラやFlowコンパイラ、Babel、SWC、esbuildなどです。
  • 次のステップは、いわゆるダウンレベルコンパイルです。
  • Ecma International のTC39やJavaScriptエンジン開発者、コミュニティの尽力によって、JavaScriptは毎年大きく進化し、プログラマにとってより便利なものへと成長しています。
  • まだ記憶に新しい便利な機能としては、ES2020 の Optional Chaining や ES2022 の Array.prototype.at などがあるでしょうか。GitHub の tc39 オーガナイゼーションの proposals リポジトリの finished proposals の一覧を見ると JavaScript が年々便利になっていっていることがわかると思います。
  • 我々プログラマーは生産的な仕事をするためにこのような便利な機能をできるだけ早く使いたいわけですが、フロントエンド開発の性質上、それが難しいことがあります。
  • サーバーサイドの開発では自分たちの書いたコードを実行するランタイムを、基本的には自分たちで決めることができます。しかしフロントエンドではランタイムはユーザーが持っているウェブブラウザです。したがって、新しい言語機能をすぐに使えないケースがよくあります。
  • そういった場合には、新しいJavaScriptのコードを古いバージョンのJavaScriptでも動くように変換する必要があります。これがダウンレベルコンパイルです。
  • スライドをご覧ください。これは Optional Chaining と Array.prototype.at をそれぞれ ES2015 でも動くように変換する例です。
  • このステップによって我々JavaScriptを書くプログラマは、自分が使うJavaScriptのバージョンを決定する自由を手に入れているわけです。
  • このステップを行う具体的なソフトウェアは、さきほどと同じで、TypeScriptコンパイラやFlowコンパイラ、Babel、SWC、esbuildなどです。
  • 次のステップは、モジュールのバンドルです。
  • 以前のECMAScriptにはモジュールの機能がなく、ウェブブラウザ上のJavaScriptでモジュールを使った開発をするのは不可能でした。想像してもらえればわかると思いますが、モジュールなしで大規模なソフトウェアを開発するのは中々につらい仕事です。
  • そこで、モジュールバンドラーというソフトウェアが登場しました。プログラマはモジュールで分割されたコードを書き、モジュールバンドラーを使ってそれをあらかじめブラウザが実行できる形式にバンドルします。
  • 実際のユーザーのブラウザでは、そのバンドルされたコードが実行されるというわけです。
  • つまり、ダウンレベルコンパイルと同じような考え方とも言えます。
  • スライドをご覧ください。これは簡単なモジュールのバンドルの例です。
  • このステップを行う具体的なソフトウェアは、browserify、webpack、parcel、Rollup、Vite、esbuild などがあります。
  • 最後のステップはミニファイです。
  • ユーザー体験を最大化するためには、配布されるJavaScriptのサイズは小さい方が良いです。当然ですが大きなサイズのファイルをダウンロードするのには時間がかかります。
  • スライドをご覧ください。これは簡単なミニファイの例です。
  • ミニファイには色々な手法があります。空白やコメントの除去、変数名の短縮、より短い等価なコードへの変換、デッドコードの除去などです。
  • このミニファイのステップは十分に普及していますが、性質上必須というわけではありません。そのままでもJavaScriptのサイズが十分に小さい場合や、難読化の必要がない場合には不要です。
  • このステップを行う具体的なソフトウェアは、terser、UglifyJS、SWC、esbuild、Closure Compiler などがあります。
  • さて、ここまででJavaScriptのビルドステップについて説明しました。昔からなじみのあるwebpackとBabelの組み合わせも、最近のNext.jsも、大まかにはこのようなステップを経て、JavaScriptをブラウザで動く形にまで変換しています。
  • ここからは、どのような機能がJavaScriptやWebに備わっていれば、このようなビルドステップから解放されることができるのか、そしてそれは現実的であるのかについて考えていきましょう。

JavaScriptのビルドステップを取り除くためには

  • 今説明したすべてのビルドステップを取り除くことができれば、私たちはJavaScriptを直接記述できるようになるということです。
  • では、それぞれのステップについて、どのようにすれば取り除けるか検討していきましょう。
  • 最初は、TypeScriptの型を除去するステップを取り除く方法について考えましょう。
  • つまり、型アノテーションが書かれたコードをそのままブラウザで動かす方法、ということです。
  • 実は、TC39にはこのための Type Annotations という提案がすでに存在しています。この Type Annotations は JavaScript のコードの中にTypeScriptやFlowのような型アノテーションを書けるようにする提案です。
  • この提案で追加される型アノテーションはJavaScriptエンジンにとってはコメントのようなもので、ランタイムによる型チェックが行われるわけではありません。型チェックは今と同じようにTypeScriptコンパイラなどの別のソフトウェアが担うことになります。このような特徴もあって、この提案は以前は Types as Comments と呼ばれていました。
  • この提案があれば、TypeScriptのような構文でプログラムを書くことができ、型アノテーションを除去するためのビルドステップは必要なくなります。
  • その型チェックは別のツールで行う必要がありますが。
  • しかし、この提案は影響範囲や議論する観点が多すぎて、中々進捗していません。MicrosoftのTC39メンバーによってこの提案が発表されたのは2022年3月のことですが、今現在でもまだTC39プロセス上でStage 1のままです。
  • さて、次はJSXをJavaScriptの関数呼び出しへと変換するステップを取り除く方法について考えます。
  • これも、考え方としては Type Annotations と同じです。JSXをJavaScriptの関数呼び出しとして解釈できる機能を、JavaScript自体に実装するしかありません。
  • 今のところ、TC39プロセス上にJSXを追加するという提案は正式には存在していません。しかし TC39のフォーラム( https://es.discourse.group/) や Matrix Room では議論が続いています。特に今月のTC39ミーティングのアジェンダではJSXについて議論するためのMatrix Roomが紹介されるそうなので、議論を続けていく気がある人はいるようです。
  • しかしながら、議論を追いかけると、シンタックス・セマンティクスの両面で懐疑的な姿勢を取っている人もおり、良く進捗しているとは言い難い状況かと思います。
  • 次に、ダウンレベルコンパイルのステップをどのように取り除けるかを考えます。
  • Intenet Explorer がその役目を終えてMicrosoftによるサポートもなくなった現在では、以前と比べて多くのユーザーがいわゆるエバーグリーンブラウザを使うようになっています。
  • Google Chrome や Microsoft Edge では、基本的に放っておけばバージョンが勝手に上がっていきます。Apple Safari はそこまでではないですが、MacOS のバージョンを上げれば Safari のバージョンも勝手に上がるようになっています。
  • それに伴ってダウンレベルコンパイルの必要性はそもそも下がってきているのではないかと考えています。提供するサービスのユーザー層によっては、すでにダウンレベルコンパイルなしでも十分に便利なJavaScriptを書けるのではないでしょうか。
  • 現実的には、新しすぎるJavaScriptの機能を使うとユーザーの環境では動作しない可能性が高いので、ESLintなどの静的解析で、それらの機能を使えないように制限する必要があると思います。eslint-plugin-es-x などを使えば、特定のバージョンのJavaScriptの機能を使えないように制限をかけることができます。
  • さて、次はモジュールのバンドルのステップを取り除く方法について考えましょう。
  • さきほどはモジュールのバンドルというステップの必要性について、ダウンレベルコンパイルと同じような側面からのみ説明しました。
  • その観点で言えば、やはりダウンレベルコンパイルと同じように、すでにモジュールのバンドルは必ずしも必要とは言えなくなってきています。
  • なぜなら、多くのウェブブラウザはECMAScript Modulesを直接実行できるようになっているからです。モジュールを使ってコードを書いて、それをそのまま配布できるはずです。
  • 多くのフレームワークが今でもモジュールのバンドルをし続けているのはおそらく別の理由で、それはパフォーマンスの改善です。
  • ECMAScript Modulesを使ってコードを書いた場合、基本的にはimportのたびにfetchが発生することになります。もしもファーストビューのために数百からなるモジュールを読み込む必要があったら、結果としてユーザー体験を大きく損なうことになるでしょう。
  • そのため、事前にモジュールを適切な大きさに区切ってバンドルしてから配布しているというわけです。この観点では、ブラウザがECMAScript Modulesをサポートするようになった後でも、モジュールのバンドルというステップは依然として必要です。
  • この import による fetch のたびに発生するオーバーヘッドという問題を解消するために、Subresource loading with Web Bundles や Bundle Preloading といった技術が提案されていました。
  • これらの技術は、複数のJavaScriptなどのファイルのHTTPレスポンスを、Web Packaging の技術を使って一つのファイルとして返し、ブラウザはそれを個別のHTTPレスポンスとして解釈できるようにするものです。
  • この技術があれば、意味論としてはそのままで、多くのモジュールから構成されたJavaScriptファイル群を、実体としては一回のHTTPリクエストで取得できるようになるというわけです。
  • しかし Web Packaging 関連の技術はこれ以上進むことはないらしく、したがってこれらの技術が実用されることはないでしょう。
  • 最後に、ミニファイのステップをどのようにして取り除けるかを考えてみましょう。
  • これは本当に難しい問題です。ミニファイのステップでは、JavaScriptのコードを解析した上で等価かつ小さいものに変換します。これを汎用的な圧縮技術で実現するのはおそらく困難でしょう。
  • しかし実は、このような問題を解決するための Binary AST という提案が TC39 に存在しています。Binary AST では、名前のとおり JavaScript の AST を表現できるバイナリ形式を新しく作成して、通信経路には JavaScript のテキストファイルではなくそのバイナリデータが流れるようにします。
  • これによって、クライアントがダウンロードするデータのサイズを減らすことができ、パースの時間も削減することができます。
  • しかしこの提案もほとんど動いていないようです。おそらく、各エンジンでの実装や標準化が難しかったのだと思います。
  • 以上で、JavaScriptの各ビルドステップをなくすための様々な手法を考えました。もちろん、この発表を聞いてくれている人の中には、今話した話題について私よりももっと多くのことを知っている人がいると思います。ぜひ、懇親会などで話しかけてください!

私の考え

  • ここまで話して、一つわかったことがありますね。
  • 少なくとも現段階ではほとんどのビルドステップを取り除くことはできないし、少なくとも短期的な未来にはそこまで希望はない、ということです。残念です。
  • ではここからは、「そもそもJavaScriptをそのままブラウザで動かす必要はあるのか」について考えていきましょう。
  • 私の考えでは、今のところその必要はないのではないかと考えています。
  • 以前までは、それとは全く逆の考え方をしていました。
  • なぜなら、ビルドステップを構成するソフトウェアの開発は難しく、その開発がサステナブルなものではなかったからです。
  • 以前までは、webpackとBabelやTerserが最も使われているツールだったと思います。これらはOpenCollectiveなどでお金が支払われているとは言え、コミュニティのボランティアによって開発されていました。
  • しかしここ数年間で、ビルドステップを構成するソフトウェアの開発を取り巻く環境が大きく変わりました。
  • 今ではVercelやByteDanceなどの会社がJavaScriptのビルドステップを構成するツールに大きく関わっています。
  • もちろん、これらの会社の存続や、開発に対する姿勢を完全に信頼できるわけではありませんが、以前よりはサステナブルなものになっているのではないでしょうか。
  • 5年後10年後にどういう状況になっているのか非常に楽しみでもあります。
  • 今説明したように、JavaScriptがブラウザでそのまま書けるようになる必要は、少なくとも今のところ、実用性の観点ではなさそうだとは思っているのですが、私個人としては「そうなってほしいなあ」とは思っています。
  • というのも、どうしてももったいないと感じてしまうのです。
  • たとえば、ブラウザでのECMAScript Modulesの振る舞いはECMAScriptとHTMLですでに標準化され、多くのブラウザでちゃんと動きます。
  • そのような状況であるにもかかわらず、それらの機能をプロダクションで利用できていないのは、HTMLとJavaScriptのポテンシャルを活かしきれていないように思えて、どうにももったいないと感じてしまうのです。
  • ここまで話を聞いてくれてありがとうございます。

まとめ

  • 最後にまとめです。
  • 最近流行っているフレームワークでは、プログラマがJavaScriptを直接書かないものが多いです。
  • その裏側には、いくつかのビルドステップがあります。
  • JavaScriptやWebにいくつかの新しい機能が備わっていれば、それらのビルドステップを取り除くことができそうです。
  • しかし、実際にはそれらの技術の多くはまだ実用的とは言えず、短期的には中々厳しそうです。
  • ここ数年の間で、VercelやByteDanceなどの会社の動きによってビルドステップを構成するためのソフトウェアの開発を取り巻く環境が変わってきています。
  • したがって、ビルドステップを構成するソフトウェアの開発負荷という観点では、JavaScriptをブラウザで直接書けるようにする必要はなくなってきていると私は考えています。
  • しかし、私としてはもったいないと感じる部分もあります。HTMLやJavaScriptのポテンシャルを活かすことができていないと感じるからです。
  • 私の発表はこれで以上です。人によって色々な感じ方がある内容だったと思います。もっと議論したい人は懇親会でぜひ話しかけてください!また、ユビーのブースにもおりますので、声をかけてください!

Discussion