https://www.youtube.com/watch?v=PFeR332LurM&list=PLwM1-TnN_NN4SV6DEs4OtfA51Up6XzTfB&index=5
しまぶーのモダンJavaScriptの歴史動画をまとめます。
なぜJavaScriptを使う必要があるのかを勉強する
前回の記事
前回はそもそもなぜJavaScriptが必要なのか、そしてJavaScriptの誕生からサーバーサイドJavaScriptの動きをまとめました。
流れを簡単に復習します。
CommonJSでいろんなAPI仕様が作られていく。その中でも特にフロントエンド開発にも大きく影響する「モジュール」からまとめていく。
モジュールとは
モジュールはただのファイルです。1つのスクリプトは1つのモジュールです。モジュールは相互に読み込んだり、exportとimportを使用して機能をやり取りしたり、あるモジュールの関数を別のモジュールから呼び出したりすることができます。
モジュールのありがたみをわかりやすくするために、JavaScriptの現状の問題点をストーリーで見ていく。
<JavaScriptの現状の問題点>
一つ目の名前空間の問題を解決するのがモジュール。依存関係は後述するパッケージ管理(npm)によって解決する。
モジュールがあるとこうなる。
<a.js>
var foo = 123;
module.exports = foo;
<b.js>
var foo = "foo";
{
<c.js>
var foo = require("./a");
console.log(foo); //123
このように値を読み込むファイルを明示的に指定することで名前空間の問題を解決することができる。
<スコープについて>
モジュールにはスコープの概念がある。JavaScriptのモジュールのスコープは1ファイル単位で、ファイル内の変数や関数は外部に影響を及ぼさない。
モジュールによってJavaScriptの名前空間の問題が解決された。しかしこれはあくまでCommonJS(サーバーサイドでもJavaScriptをかけるようにしようねというプロジェクト)の話で、ブラウザのJavaScriptで使うことができない。ブラウザのJavaScriptはずっと名前空間は1つで、モジュールが使えないので問題は依然として残り続ける。
モジュールのおかげで名前空間の問題が解決されて、機能が細かく分けられるようになった。さらにいろんな機能を組み合わせて、便利なことができるようになった。
機能が細分化されていくと今度はそれらを共有して使用するニーズが生まれる。これは他のサーバーサイド言語ではすでに実現されていること。Node.jsでもこれが求められた。
共有したい機能の単位がパッケージである。1ファイルの場合もあれば、ディレクトリの場合もある。
Node.jsで色んなパッケージが開発されるようになるとそのパッケージのバージョンを管理したり、共有するためのシステムの必要性が生まれた。そこで登場するのがパッケージ管理システム。
ローカル環境にインストールしたパッケージを更新できる。またパッケージを検索できる。
パッケージを指定してローカルにインストールすることもできる。反対にローカルから削除することもできる。
パッケージに必要な別のパッケージを自動的にインストールや更新することができる。
設定を書くことによって、上記3つを自動で行うことができる。毎回手動でパッケージを入れたりする必要がなくなり、チームでの環境を簡単に揃えたりすることができる。
2010年にlsaac Z. SchlueterによってNode.jsのパッケージ管理システムであるnpmが誕生した。Node package managerの略語である。
名前空間の問題はモジュールで解決され、依存関係の問題はnpmで解決された。Node.js製のツールがどんどん開発されていった。
Yahooが2009 - 2010年頃からNode.jsを採用
LinkedInが2011年にNode.jsを採用
PayPalが2013年にJava → Node.jsに移行
ブラウザ側でもモジュールを実現しようと努力していた。
上記4つのモジュールでは、IIFEというものを利用していた。
即時関数は、関数を定義すると同時に実行するための構文。
(function(){
var foo = "foo";
})();
foo; //foo in not defined
JavaScriptには、グローバルスコープと関数スコープがある。
即時実行関数式によって任意にスコープを狭めることで、擬似的にモジュールのような仕組みを作っていた。
言語仕様としてのモジュールではないので、名前空間の問題も完全に解決されるわけではなかった。
ブラウザでのモジュールの扱いを改善するためにAMDという仕様が2009年頃に誕生した。この仕様を実装しているものがRequireJS。
ブラウザ環境での実行を考慮し、依存関係の解決(IIFEではここの解決ができていなかった)及び遅延ロードに対応した仕様。
define(['jquery', 'underscore'], function ($, _) {
function a(){}; // public
function b(){}; // private
return a;
});
AMD形式でブラウザでもモジュールが実現。しかも依存関係も解決することができる。次に欲しいのは共有やバージョン管理のためのブラウザ向けパッケージ管理システム。
クライアントサイド開発向けのパッケージ管理システムBowerが2012年に誕生した。Bowerがあれば、npmと似たことができる。
ほとんどのBowerのパッケージは、IIFEモジュールかAMDモジュール(ブラウザ向けに作られたモジュール)を利用している。
サーバー側の機能とは互換性がなく、AMD形式の構文はCommonJS形式と比較して冗長。
依存関係が多いと、メンテナンスが大変で、パフォーマンス面でも問題があった。
どのパッケージがどう依存しているかをユーザーが手作業で定義する必要があった。
同一ベージ内にある同じパッケージの異なるバージョンをサポートしていなかった。(一つのページ内で、同じパッケージの異なるバージョンを使用できなかった。)
「CommonJS形式で書かれたものを事前にブラウザ向けに変換しよう」
CommonJS形式で書かれたものをブラウザ向けにバンドルするツールBrowserifyが2011年に誕生。
Browserifyによって、ブラウザでもrequire構文を用いてモジュールを書くことができるようになった。Node.jsのパッケージがブラウザ向けに移植された。
元々npmはCommonJS形式で書かれたパッケージが多く、ブラウザでは使えなかったが、Browserifyでブラウザ向けに変換できることで、ブラウザでもnpmが使われるようになった。
新たなバンドルツールwebpackが2012年に誕生。Browserifyより高機能で現在でもwebpackが主流。(最初からBrowserifyよりwebpackの方が高機能だったわけではないみたい)
主にJavaScript向けだが、対応するローダーがあれば、HTML、CSS、画像などのフロントエンドのアセットも変換(バンドル)することができる。
バンドルする前はファイル同士に依存関係があるが、バンドルした後のファイルには依存関係がない。
CommonJS形式モジュールをバンドルするのがBrowserify。
JSに限らず、なんでもバンドルするのがwebpack。
webpackを使うと、コードを複数のchunkに分割できる。chunkは実行時に非同期的にロードされる為、最初のロード時間を短くすることができる。
今までの話は全てJavaScriptの言語自体が持っている仕様ではなく独自の仕様である。なので、言語仕様としてのモジュールが求められていた。
ES2015でJavaScriptの言語仕様として、モジュールの仕組みがついに導入された。
CommonJS形式ではrequire構文を使用するが、ESModulesでは、importという構文を使用する。
しかし...ESModulesができても、まだほとんどのブラウザでサポートされていなかったので、モジュールバンドラーが必要な流れは変わらない。
言語仕様としてモジュールが使えるようになり、独自のCommonJS形式でコードを書くよりも、ESModulesでコードを書きたいという需要が高まる。
バージョン2でESModulesをネイティブサポート。ESModules形式でモジュール間のやり取りが可能に。
webpackがESModulesに対応したことによって、開発時はimport構文でコードを書くことができる。そのimport構文をバンドルすることによって、ブラウザで動くようなコードになる。
ESModulesはEcmaScriptの標準仕様なので、現在ではwebpackを通さなくても、IE以外のモダンブラウザとNode.jsのversion12系以上では標準で使用可能。
ここまでの話はBundleの話、次はCompileについて説明していく。
let, const, class, Promise, アロー関数, 分割代入, スプレッド構文, テンプレート文字列...などのたくさんの仕様が追加された。しかしすぐに使えたわけではなかった。
ES2015等で書かれたコードを従来の環境でも動くように古いJavaScriptに変換するコンパイラ。Babel(元は6 to 5 という名称)が2014年に誕生した。
webpackの設定でbabelを一緒に使うことができた。モジュールが使えるようになるだけでなく、ES2015の新機能まで使えるということで、急速にJavaScriptが便利になっていった。
コンパイルが当たり前になることで、ES2015以外にも便利なパッケージが流行り出す。
ブラウザでモジュールを使う為に模索した結果、コードを事前に変換することが主流になった。コードを事前に変換すると、モジュールを使える以外にもいろんな恩恵があるということ。
事前に変換というパラダイムシフトによって、JQueryが使われなくなったと考えられる。事前に変換することでモジュールも使えるし、ESの新機能やTypeScriptなどが使える。
はい、ということでモダンJavaScriptの歴史をまとめていきました。
現在使われているwebpackやBabel、ReactやTypeScriptがなぜ生まれたのか、その裏にはどんなストーリーがあったのかをいい感じに知ることができました。
最後までごらんいただきありがとうございました〜!
@ 酒井悠宇