RequireJS の歴史
私は Dojo Loader に多くの取り組みをしてきました。通常の Dojo Loader は同期的な XMLHttpRequest (XHR) 呼び出しを使用していました。しかし、XHR ローダーは同一オリジンポリシーのため、他のドメインから Dojo モジュールをロードできませんでした。そこで、RequireJS が使用するものと似た関数ラッパーを注入するビルドステップを必要とする xdomain ローダーを作成しました。ただし、i18n バンドルのロードや dojo.requireIf の動作のため、より複雑でした。より複雑な i18n および requireIf の要件と、すでに世の中に存在している多くの Dojo モジュールの存在のため、Dojo コミュニティが関数ラッパーを手動で記述するモジュールを検討するとは思えませんでした。
しかし、同期的な XHR ローダーには、デバッグが難しくなるなどの問題もありました。2009年、David Mark は、この問題を解決するために、ページが読み込まれる前に document.write() を使用してモジュールをロードすることを Dojo に提案しましたが、これは、必要な依存関係が現在のモジュールが実行されるまでロードされないことを意味していました。モジュールの定義の一部として依存関係を参照する場合、これはエラーを引き起こす可能性があります。そのため、関数ラッパーが必要になりました。Dojo コミュニティは、特に一部の API を壊す可能性のある Dojo 2.0 を検討しているため、関数ラッパーを検討することに前向きでした。私は、dojo-contributors メーリングリストで RequireJS (当時は RunJS と呼ばれていました) の詳細をいくつか詰めました。また、Mike Wilson は当初、プレーンファイルもロードできる、より一般的なローダーを推進していました。異なるコンテキストを許可することも含めて。
YUI 3 の use() 関数も require と非常によく似ており、use() の API (コードではなく) も RequireJS の構造に影響を与えました。YUI はファイルパスに直接対応しないモジュールのラベルを使用しているように見えるため、RequireJS の方がよりジェネリックであると考えています。また、依存モジュールを関数定義への引数として明示的に渡すことが好きでした。これにより、JSLint のようなリンティングツールをより効果的に使用できました。
私は当初、CommonJS モジュールで動作するものを望んでいましたが、それらのモジュールはサーバーサイドの JavaScript 環境で可能な同期モジュールローダーを想定して構造化されているようでした。しかし、私は主にブラウザでうまく動作するものに関心があり、それはスクリプトタグを使用できるように関数ラッパーが必要であることを意味していました。同期的な XHR を使用することは、新しい開発者や、ブラウザ間でのデバッグを容易にしたい人にとってはあまり親切ではありません。また、プレーンなスクリプトタグの読み込みよりも遅くなる可能性があります。Adobe AIR のような一部の環境では eval() が許可されておらず、ほとんどの開発者は eval() が悪であり、避けるべきだと教えられています。
私は RunJS として RequireJS の種を作成しました。より多くのコードの再利用を可能にするために CommonJS モジュールとの同期を試みるにつれて、CommonJS Transport/C 提案を提案しました。トランスポートフォーマットにより、従来の CommonJS モジュールをブラウザで最適に機能するフォーマットにマッピングできます。その後、RunJS コードを RequireJS に変換して、Transport/C 提案の API に合わせました。
トランスポートフォーマットの実装過程で、CommonJS モジュールが命令的な require() の使用を許可していることがより明確になりました。これは Web 上ではぎこちないものでした。多くのケースが Web コンテキストでは機能せず、より良い解決策は、そのようなケースのためにコールバックベースの require を許可することでした。しかし、CommonJS メーリングリストの一部の参加者は、コールバックスタイルの require でも命令的な使用を維持したいと考えており、それが API を不必要に冗長にしました。
Transport/C 提案の調整がいくつかあり、Kris Zyp は匿名モジュールをそのフォーマット内で動作させる方法を突き止めました。その時点で、Kris はそれが単なるトランスポートフォーマットではなくモジュール API 提案として機能すると感じ、CommonJS Wiki に非同期モジュール定義 (AMD) API の提案を作成しました。その API の議論中に、Tom Robinson はファクトリー関数の依存関係をスキャンするために Function.prototype.toString() を使用することを提案しましたが、Tom は一般的に AMD API を気にしていませんでした。toString() スキャンは、すべての JS 環境で使用可能な toString() 値をサポートしていたわけではなかったため、簡略化された CommonJS ラッピングとして AMD API に組み込まれました。
CommonJS メーリングリストの一部の参加者は、AMD API 提案が CommonJS モジュールの元の目標と一致していないと感じていました。これは、CommonJS モジュールで完全な命令的な require() スタイルを維持していなかったためです。彼らはまた、この提案がメーリングリストに突然現れて実装され、不適切に普及されたと感じていましたが、私の視点からは提案としてマークされており、他の CommonJS 提案の実装について話している人もいました。
コミュニケーションの破綻が十分に大きかったため、CommonJS メーリングリストで AMD について議論を続けるのが困難になりました。しかし、私たち Web 開発者にはそれでもその価値を認める人が十分にいて、ローダープラグインやコールバック require に関する API 作業がまだ残っていたため、amd-implement メーリングリストと amdjs Github グループが設立され、それらの議論が継続されました。
amd-implement メーリングリストを通じて、コールバック require とローダープラグインの API がより明確になり、一連の単体テストが作成されました。より多くの AMD 実装が作成されました。Dojo はすでに AMD を使用するためのコード変換プロセスに入っており、MooTools や EmbedJS などの他の人々もそれを取り入れました。AMD ローダーは、モジュール化された JS ローディング機能を必要とする jQuery コミュニティの間で勢いを増しました。
AMD には現在、健全なエコシステムが存在しています。私は、RequireJS に堅牢な実装を提供し、Web にうまく適合するようにすることで、AMD の推進を支援し続けています。