TypiaのBundle Sizeを大幅に削減した話 (65.99 KB -> 2.53 KB)
TL;DR
- ちょうど1ヶ月前に
Typia
に commit をはじめてからというもの、Bundle Sizeの削減に取り組んできました - 大幅に Tree-shaking が改善し、Bundle Size が削減されました (65.99 KB -> 2.53 KB!)
- Bundle Size が気になる Frontend や Edge Worker にも安心して使えるようになりました
- 今後も
Typia
への commit を続けていきます。
はじめに
約1ヶ月前にこんな記事を書きました。
Typia
については上の記事を読んでいただけると嬉しいのです。
簡単に Typia
について説明すると、Typia
は TypeScript 向けのValidation Library です。
ただし、既存の Library とは違い、Typia
は TypeScript の型システムからValidation Logic を生成するという特徴があります。
- Library ごとの独自の記法を用いることなく、TypeScript の型システムをそのままValidationに使用できる
- ビルド時に Validation Logic を生成するため、非常に高速である
という特徴があります。
Bundle Sizeが大きすぎる問題
さて、上にあげた記事の最後の部分で、Typia の Bundle Size が大きい問題があると書きました。
実際、Valibotの作者である Fabian Hiller 氏の論文 によると、Typia
は高速ではあるものの、Bundle Size が大きいという問題がありました。
実際に issue にもなっていました。
この Bundle Size 問題について、ここ1ヶ月ほどかけて改善をしていきました。
この記事では、Typia
の Bundle Size を削減するために行った手法を紹介します。
振り返ってみると、よく知られた手法だったり、当たり前のことだったりしますが、自分にとっては新たな学びが多かったので、宣伝を兼ねて記事にしました。
改善結果
と、その前に、先に結果を書いてしまいます。
比較にはValibotの作者である Fabian Hiller 氏の論文が行った実装をベースに以下のような手を加えたものを使用しました。
- 論文に使われた Schema に加え、よりシンプルな Schema を追加
- Bundle Size の計測には
rollup
を使用。terser
で Minify をした - 現実に則し、無圧縮の Bundle Size と
gzip
圧縮後の Bundle Size を計測
実際のコードは以下のリポジトリにあります。
自分がビルド環境に commit しはじめてからのバージョンごとの Bundle Size は以下のようになりました...
Typia Version | Simpler Schema | Simpler Schema (Gzip) | Large Schema | Large Schema (Gzip) | Notes |
---|---|---|---|---|---|
6.0.5 | 65.99 KiB | 14.51 KiB | 74.26 KiB | 15.43 KiB | Only CJS |
6.0.6 | 36.47 KiB | 10.1 KiB | 44.75 KiB | 11.03 KiB | First ESM Support |
6.4.0 | 6.76 KiB | 2.69 KiB | 15.04 KiB | 3.64 KiB | ESM with file splitting |
6.4.1 | 2.53 KiB | 1.1 KiB | 10.8 KiB | 2.06 KiB | Enable sideEffects=false
|
valibot(v0.35.0) | 4.01 KiB | 1.43 KiB | 6.05 KiB | 1.89 KiB | 参考 |
うおお! Typia
の Bundle Size が大幅に削減されました!!!
さらに、Schema によっては valibot
よりも小さくなっていることもわかります。
では、それぞれの version で、どのような変更を行ったのか見ていきましょう。
6.0.6
6.0.5
以前は、Typiaは CommonJS (以下 CJS) のみを dist
として提供していました。
しかし、6.0.6
からは ESM も提供するようになりました。
そもそも、 CJS では tree-shaking が効きづらいという問題があり、ESM としての提供は必要だと考えました。
このバージョンからは rollup
で ESM 形式の compile を行うようになりました。
6.1.0
サイズ変更には関係のない余談
このバージョンから ドキュメントでは unplugin-typia
が setup の公式方法として紹介されるようになりました。
また、一部 Typia
が依存している CJS 形式で配布されている Library の取り扱いについても対応が行われました。
Random generator を使う際にはこの最適化が効果的です。
6.4.0
6.4.0
では、ESM において、TypeScript のファイルの構造を保ったままで mjs ファイルを生成するよう rollup
の設定を変更しました。
それ以前のrollup
の設定では、ビルド時に全ての TypeScript ファイルを一つの index.mjs
にまとめていました。
これでも問題ないだろうと考えていましたが、実際にはファイル分割を行うことで、Tree-shaking が効果的になることがわかりました。
なぜこのような Bundle Size の差が出たのかというと、Typia 内部で namespace import が多用されていたためです。
元々、Typia
の内部では namespace が多用されていました。
namespace とは以下のような構文のことです。
namespace A {
export const a = 1;
export const b = 2;
}
これは一見便利そうに見えますが、namespace は JavaScript の構文ではないため、compile の結果には余計なコードが含まれてしまい Bundle Size が大きくなる原因になります。
これに関して、namespace を使わず、namespace import を使うよう過去の PR では対応されていました。
namespace import とは以下のような構文のことです。
import * as A from './A';
import * as B from './B';
export { A.foo, B.kuu };
この方法ならば、ビルド時に Bundler が元のファイルを探してきて、tree-shaking が有効になります。
一見効果的なように見えます。
しかし、提供する mjs ファイルを一つにまとめてしまうと、tree-shaking が十分に効かないことがわかりました。
これは以下の issue で議論されています。
この問題を解決するために、6.4.0
では rollup
の設定を変更し、元の TypeScript ファイルの構造を保ったままで mjs ファイルを生成し、これを提供するようにしました。
具体的には preserveModules: true
を有効にして対応しました。
また、そのほかにもいくつかの設定を見直しました。
(↑めっちゃ必死に必要性を訴えているのがわかる)
6.4.1
6.4.1
では、package.json の sideEffects
を false
に設定しました。
sideEffects
は以下のような設定です。
{
"sideEffects": false
}
これは、このパッケージが副作用を持たないことを示すものです。
sideEffects
に関しては、Typia
のコード内で /*#__PURE__*/
というコメントが至る所で使われているため、設定は不要だと考えていました。
しかし、実際に最小環境を作成して検証をしてみると、sideEffects
を設定していない時は、依存関係の一つである ret.js
という Library が常に含まれてしまうことがわかりました。
ret.js
は Typia
の random generator の時にのみ使用される Library であり、それ以外の場面では使用されません。
明示的に sideEffects
を設定すると、ret.js
が含まれなくなり、Bundle Size がさらに削減されました。
この設定については以下の記事が参考になりました。
7.0.0
...?
おそらく内部の実装に手を入れずに Bundle Size をこれ以上削減するのは難しいと考えています。
そのため、7.0.0
では内部のコードのリファクタリングを行い、さらに Bundle Size を削減する予定です。
楽しみですね!
まとめ
Typia
の Bundle Size を削減するために、以下のような手を加えました。
- ESM 形式での提供
- 実装の構造を保ったままでの ESM ファイルの生成
- package.json に
sideEffects=false
を設定
これにより、Bundle Size が大幅に削減されました。
昨今では、Frontend や Edge Worker など、Bundle Size が気になる環境が増えてきています。
Typia
はこれらの環境でも安心して使えるようになりました。
ぜひ、お試しください!
余談
作者曰く、Typia
は nestia
という Library のために作られたものだそうです。
なので、出自が Backend であることがわかります。
Backend 用途では、Bundle Size はあまり気にならないかもしれません。
今回の Bundle Size の削減、および unplugin-typia
の開発により、Frontend への導入のハードルが下がり、より多くの人に使ってもらえるようになると嬉しいです。
宣伝 GitHub Sponsorsを始めました
この度GitHub Sponsorsを始めました。
Typia
、 unplugin-typia
を含め、そのほかにも色々 Library 等のメンテナンスをしています。
もしよろしければ、スポンサーになっていただけると嬉しいです!
Discussion