おはこんにちばんわ。
今回は、「e18e」というプロジェクトについて興味を持ったので、深掘りしていきたいと思います!
READMEやdocs配下のmarkdownを日本語に訳しながら見ていきたいと思います。
e18eとは?
e18e(Ecosystem Performance)プロジェクトとは、JavaScriptのパフォーマンス向上に情熱のある団体や個人を引き合わせるプロジェクトです。
この場では、依存関係からパフォーマンスの最適化まで、多くの継続的な取り組みが行われています。
我々の目的は、エコシステムを通してパフォーマンスの重要性を広めることで、貢献、アイディア、知識の場を発展させることです。 願わくば、このコラボレーションによって依存関係の選択などが及ぼす影響を見える化したいです。
現在、3つの観点にを注視しています:
- cleanup: エコシステムを用いている依存関係やよく知られているツールやライブラリの最適化
- speedup: 私たちの多くが依存している一部のエコシステムの速度向上
- levelup: 私たちが普段使っている既存のツールやライブラリに代わる、モダンで軽量なものの文書化と、その提供。
私たちが普段使っている既存のツールやライブラリに代わる、モダンで軽量なものを文書化し、提供する。
Contributing
詳しくは、コントリビューションガイドをご覧ください。
Credits
- cleanups ドキュメントは、James Garbutt さんの「ecosystem-cleanup」の一部を基にしています。
- ドキュメントサイトは、Vite docsを使っています。
- このリポジトリは、Anthony Fuさんのおすすめです。
cleanup
JavaScriptのエコシステムは、良くも悪くも巨大に成長してきました。我々は非常に多くのパッケージを使うことができ、自分たちが望む選択をできますが、長い間、膨大な量の負の遺産を作ってしました。
このほとんどが、過剰だったり、膨れ上がったり、アクティブなメンテナンスができなくなったパッケージで形作られています。
現在、パッケージ最適化で勢いのある例として、 @43081j's ecosystem-cleanup があります。 このプロジェクトは、多くの様々なオープンソースプロジェクトの上流工程に貢献し、促進することを目的としています。
このプロジェクトの目的は:
- 現在のパッケージの最新化
- 余剰パッケージからの移行
- 動いていないパッケージからの移行
- 軽量もしくは高速な代替品(パッケージ)への移行
コントリビュートのためのガイド
このガイドは、それらのエコシステムを発見し、コントリビュートの発展の手助けを行う場所です。
上流パッケージにおいて、いくつかの共通の発見テクニックと解決すべき課題を説明します。
発見方法
我々は、以下の判断基準に合うパッケージを探しています。
- 肥大している
- もはやメンテナンスされていない
- 古い
- 遅い
- 余剰
まずは、改善したパッケージを選ぶことです。例えば、viteを選んだことにしましょう。
最初のステップは、このパッケージの現在の状態をdiscover(発見)することです。
npmgraph
やpkg-size
を使うことができます。
この場合では、viteの依存関係のグラフとサイズを可視化できます。
これによって、複雑な依存ツリーや全体のインストールサイズ(インストール時に落としているデータの量)を知ることができます。
Rollup bundle
例では、vite
は(執筆時点で)とても少ない依存関係でインストールサイズを減らすことができるようです。
これは、vite
がnode_modules
から引っ張ってくるというよりは、依存関係の多くをバンドルしているからです。
このため、rollup
バンドラの分析に踏み込む必要があります。
vite
のバンドル内を可視化生成できるrollup-plugin-visualizerというプラグインを使います。
この成果物から、インポートされているバンドルやツリー内の大きな依存関係を素早く把握することができます。
依存度の深さに注目する
依存関係のグラフでは、非常に巨大な依存関係を持つサブツリーに着目すると簡単に勝つことができる(減らす対象を把握することができる)。
このことは、通常なら正しいですが、(古いバージョンのnode
など)いくつかの深い依存関係には使い所があります。依存関係のリストからツリーを削除する前に、これを考慮に入れるのを忘れないようにしてください。
改善
今、依存関係のグラフ、パッケージサイズ、バンドルサイズを可視化したので、改善について考えることができます。
(その可視化したものの中で)何を探すべきか:
- それ自身が外部のサブツリーに何も依存していない、より深い依存関係の巨大なサブツリーを持つ依存関係。
- (複数のバージョンや同じ目的を達成する依存関係を持つような)重複した依存関係
- 非常に巨大な(インストールサイズの)依存関係
vite
の例に戻ると、(この記事を書いているとき、)vite
にバンドルされているfast-grab
とglab
の両方がバンドルされているrollup
を見ることができます。
(バンドルされているのは確認できませんでした。。。)
これは、同じことをする2つのパッケージをバンドルしていることを意味しています。パフォーマンスを改善できる可能性があります!
cleanup issueを立てる
ここでのポイントは、いくつかの潜在的なパフォーマンス改善を特定できたら、このリポジトリにissueをオープンすることです。
たとえ自分で取り組むとしても、issueを立てることで視認性が向上し、仕事の重複を防ぎ、あなたを助けることができます。
もしくは、自分で取り組む時間がなければ、あなたの気づきを誰かにフォローしてもらうことも可能です。
DevMinerさんのpackage-size-calculatorを使ったパッケージレポートもissueに添付して、パッケージサイズとあなたが提案している変更の概要を説明してください。
修正
パフォーマンスの問題があるいくつかの依存関係を特定しました。
特定した改善の種類によると、修正や変更を適用するには、いくつか異なるアプローチがあります。
巨大なサブツリー
これらの依存関係では、まず最初に代替案ではなくそれが必要な理由があるかどうかを調べるべきです。
一般的なチェック観点:
- この依存関係がサポートしているnodeが、古いnodeバージョンを指定している(そして、代替品はサポートしていない)。
- 知られている代替品がない(代替品を作る良い機会だ!)。
- サブツリーにある、どの深い依存関係も必要である(多くの場合、ツリーは深いかもしれないが、モジュールを共有するなどの正当な理由があります)。
もし、それが切り替えられるなら、代替品を見つけるべきです。そのための良いリソースは、
- The module replacements repository
- tiylibs
- unjs と、もっとたくさんあります。良いものやより最適化されたものが見つかったら、我々の誰かに共有してください!
機能的に重複している
多くの場合、同じ機能を実現するために、別々のバージョンや全く別のソリューションを使用している依存関係を見つけることがあります。
別々のバージョンの場合、遅れている方を最新バージョンにアップデートするために、上流に貢献して欲しいです(どちらも最新でない場合は両方)。
別々のソリューションの場合、一方のソリューションが他のものより優れているか確認し、そうであれば、一方のソリューションへの移行を考慮すべきです。
viteの例で言うと、何がfast-glob
を使うか、何がglob
を使うかと言うことです。そして、2つのライブラリと代替案を比較し、それらの依存関係が同じソリューションを使えるかどうか、あるいはより新しく優れたソリューションを使えるかどうかを調べます。
巨大な依存関係
巨大な依存関係を見つけた場合、大抵は必要な機能よりもはるかに多くの(有用な)機能が含まれているものです。
ここでの修正は、以下のいずれかである場合が多いです:
- より粒度の粗いモジュールに分割するように上流に貢献する
- よりスリムで、より焦点を絞った依存関係に移行する
このような状況では、より軽量な(願わくばより高速な)代替品を探す価値があります。
Speedup
誰にとっても、エコシステムを最適化する上で重要なことは、スピードです。コアパッケージ、ツールやそのほかのスピードは、常に最適化でき、コミュニティ全体に利益をもたらすでしょう。
いくつかの団体や個人の方達が、以下の場で動いています。以下に幾つかをリスト化しておきます:
Linting
いくつかのパフォーマンス最適化はLinterを介して検出することができます。
ESLint
ESLintを使っていれば、e18eの取り組みの原則に強く合致するプラグインを使うことができます:
Plugin | 説明 |
---|---|
eslint-plugin-depend | 冗長なパッケージを検出し、よりパフォーマンスの良い代わりのパッケージを提案する |
eslint-plugin-barrel-files | バレル(他のファイルからたくさんのものを再エクスポートしただけのファイル)を検出する |
Biome
Biomeはe18eに合致するいくつかのルールをサポートしています。
Coding tips
アプリケーションのパフォーマンスにおいて、記載するコードは重要な役割を担います。よくある落とし穴と改善方法について、いくつかのパターンを以下に紹介します。
パフォーマンスを変更する際には、常にコードのプロファイリングを行うことを忘れないでください!
ホットコードパスの作成は避ける
現時点では、ほとんどのJavaScriptエンジンはgenerator関数(イテレータを生成する関数)呼び出しを最適化していないため、広範囲に使用すると、パフォーマンスが大きく低下します。
非同期イテレータやプレーンな配列を使用することをお勧めします。
補足
配列のチェーンメソッドを避ける
map
, filter
, reduce
などのような配列のチェーンメソッドをつなげると、多くの中間配列が生成・廃棄されることになり、ガベージコレクタの処理が増えてしまいます。また、連鎖のたびに余計な反復が発生し、必要以上の回数になることもあります。
for
ループ、for...in
ループ、for...of
ループ、またはシングルチェーンメソッドのみを使用することで、上記の注意点を防ぐことができます。
補足
Levelup
特に最近のランタイムがもたらす機能によって、多くのパッケージはよりスリムに、より速く、より焦点を絞ることができるようになりました。
webpackのようなカスタマイズ性を必要としない場合、軽量なものの代替手段としてesbuild
が導入されたように、これらのパッケージは高速かつ小規模であるためにここにあります。
この取り組みに大きく貢献しているプロジェクトもあります:
LevelupとCleanupプロジェクトの取り組みは、しばしば交差しています。これらのパッケージは、インストールサイズを大幅に削減すると同時に、すばらしいパフォーマンス向上を与えてくれるからです。
参考
Efforts
Description | |
---|---|
JS ecosystem cleanup | Efforts to provide a cleaner dependency tree in the JS ecosystem |
Tinylibs | A place for tiny and minimal libraries |
unjs | Purpose-built, high-quality JavaScript utilities, libraries, and tools |
es-tooling | A collection of tooling for the JS ecosystem |
Tools
Description | |
---|---|
eslint-plugin-depend | ESLint plugin to suggest dependencies alternatives |
eslint-plugin-barrel-files | ESLint plugin to avoid barrel files |
Biome's noBarrelFile | Biome plugin to avoid barrel files |
Biome's noReExportAll | Biome plugin to avoid re-export all |
unplugin-purge-polyfills | Unplugin to replace package imports with native code |
Package Size Calculator | CLI to calculate the size of a package after removing/replacing dependencies, or calculating the difference of two versions of the same package |