こちらは、2020年6月19日に公開された以下のドキュメントを翻訳したものとなります。
Porting a Large ES5 JavaScript Library to ES6 Modules and Rollup
2011年以降、PlayCanvasエンジンのソースベースはES5 JavaScriptの仕様をサポートしてきました。
その間にJavaScript言語や、周囲のツールエコシステムは大幅に変化しました。それにもかかわらず、PlayCanvasはES5に固執してきたのです。なぜでしょうか?その理由はInternet Explorer11です。
IE11は2013年10月17日にリリースされました。StatCounterによれば、IE11は現在でもグローバルデスクトップブラウザ市場で2.43%のシェアを占めています。PlayCanvasのコンテンツは数億人のエンドユーザーに視聴されるため、これは非常に大きな数字だといえます。
時が経つにつれてPlayCanvasエンジンのコードベースは著しく膨大化し、100,000行もの長さになりました。このように大規模なコードベースの場合、管理や構築の点で問題が発生する可能性があります。そこで、一定レベルの一貫性と構造を維持するために以下のパターンを設定しました:
Object.assign(pc, (function () {
var SomeClass = function () {
this.other = new pc.OtherClass();
};
Object.assign(SomeClass.prototype, {
someFunction: function () {}
});
return {
SomeClass: SomeClass
};
}()));
pc
は、PlayCanvasライブラリのネームスペースです。このため、ディベロッパーはこのクラスのインスタンスを以下のように作成します:
var thing = new pc.SomeClass();
エンジンを構築するため、弊社はNode.jsスクリプトを記述しました。このスクリプトは依存関係のリスト(JavaScriptファイル名)を構文解析し連結します。ここで、いくつかの問題が発生しました:
- 上記のパターンは非常に詳細なため、コードの調査が難しくなりました。
- 各モジュールでブラウザがライブラリを初めて実行する際にObject.assignを250回呼ぶ必要があり、結果的にアプリケーションの起動が遅くなりました。
- 内部エンジンコードでは、すべてのクラス名や定数へのアクセスを
pc
ネームスペース経由でおこなう必要がありました。これは、上記のパターンの内部は他のモジュールの内部を参照できず、逆もまた同様であるためです。このためエンジンコードが膨大になり、処理が遅くなりました。 - 各項目が正しい順序で宣言されているかを確認するため、依存関係ファイルは手動で注意深く順序付けする必要がありました。
- ビルドスクリプト自体がJavaScriptで約1,000行あったため、個別のメンテナンスが必要でした。
- パブリッシュ済みのエンジンに、使用されないコードが含まれていました。
弊社は、これらの問題を解決するにはPlayCanvasエンジンのコードベースをvanilla ES5からES6モジュールへ移行するべきだと考えました。このため、当初のモジュールパターンが以下のように変換されました:
import { OtherClass } from './other-class.js';
var SomeClass = function () {
this.other = new OtherClass();
};
Object.assign(SomeClass.prototype, {
someFunction: function () {}
});
export { SomeClass };
格段に良くなりました!
- pcネームスペースへの、Object.assignの不要な呼び出しがなくなりました。
- モジュール内でのpcネームスペースの使用が不要になりました。
- 依存関係は各JSファイルに明確に記載されました。
- 必要なもののみが、モジュール(およびPlayCanvasエンジン全体)からエクスポートされるようになりました。
ただ、IE11についてはどうでしょうか?IE11はES6のモジュール構文を理解しません!PlayCanvasは引き続き、厳密なES5ライブラリでなければなりません。コードベース全体をES6のモジュールフォーマットからES5のUMDフォーマットに変換するには(PlayCanvasはブラウザおよびノードの両方で使用されます)、JavaScriptバンドラを使用する必要があります。
JSバンドラには多くの選択肢があり、弊社はRollup、Parcel、ESBuildの3つをテストしました。それぞれのプルリクエストについては、以下を参照してください:
- Rollup: https://github.com/playcanvas/engine/pull/2166
- Parcel: https://github.com/playcanvas/engine/pull/2106
- ESBuild: https://github.com/playcanvas/engine/pull/2115
結局、弊社はRollupを選択しました。Parcelがテストで初期ビルドに最大16秒を要したのに対し、Rollupの所要時間はわずか3秒でした。一方、ESBuildには優れた評価が少なく、Rollupに比べて実際の導入例も乏しかったため、弊社はESBuildの採用には消極的でした。ただし、この決定は今後見直される可能性があります。この時点でPlayCanvasエンジンはES6モジュールに移行したため、バンドラの切替は非常に順調でした。
Rollupの導入によって、ビルドスクリプトは1,000行以下から100行以下へと減少しました。大幅な減少といえます。Rollupのプラグインシステムは、GLSLファイル向けのカスタム処理の記述を飛躍的に容易にしました。またC言語に類似したプリプロセッサを実行することで、デバッグのビルドやPlayCanvasエンジンのバージョンのリリースおよびプロファイリングを簡単におこなえるようになりました。
さて、次は?
これでES6のモジュールポートをマージできましたが、今後は何をおこなうべきでしょうか?
まずRollupは、PlayCanvasのコードベース内に循環依存関係が存在すると教えてくれています。
これから整理をおこなって、循環依存関係を削除したいと考えています。何がモチベーションとなるでしょうか?これらの削除によってバンドラは容易にツリーシェーキングを使用し、参照されていないコードをエンジンから削除できます。現時点では、PlayCanvasエンジンのApplication
クラスはほぼすべてをインポートし、また多くのクラスがApplication
をインポートしています。このため、たとえばパーティクルエンジンや物理エンジンなどを含まないライブラリのバージョンを構築することが難しくなっています。
循環依存関係の削除が完了したら、ES5を脱して「最新」のJavaScriptまたはTypeScriptを存分に活用したいと思います。RollupはプラグインとしてBabelまたはTypeScriptコンパイラを実行できるため、弊社は今後もES5ライブラリを提供できます。皆様からのフィードバックをもとに、この決定をおこなう予定です。ぜひ、フォーラムにご意見をお寄せください!
コメント
0件のコメント
サインインしてコメントを残してください。