JavaScriptにおけるバンドルサイズの最適化を色々調べる&試す
terser
よく聞くやつ
It removes comments, makes variable names smaller, and removes whitespace.
Some variable references and function calls can be inlined into the places they're used.
swc
最近よく聞くやつ
- Compilation
- Bundling (swcpack, under development)
- Minification
- Transforming with WebAssembly
- Usage inside webpack (swc-loader)
- Improving Jest performance (@swc/jest)
terserはminifyのみだが、swcは色々できるのか
ベンチマーク
それぞれのベンチが見たい
適当にネットで調べると次が出た
tdewolff-minify
初めて知った
圧縮率はまだまだだが、とにかく早い
google-closure-compiler
めちゃくちゃ頑張るので遅いけど、圧縮率はいいって聞いたことがある(要出典)
初めてベンチを見たけど、正直遅くてもプロダクションビルドならば圧縮率優先でもいい気がした
あんまり使われているのを聞かない気がするけどなんでだろう
gzip
ハフマン符号化で圧縮していて、CDNとかで配信する際に圧縮に使っている、はず、というぐらいの認識
brotli
Googleが作ってる、辞書を持ってるのでgzipより圧縮率がいいやつ、という認識
brotliを使ったPR出てるけどマージされてなかった
ベンチまとめ
- swc:Minzipped sizeで見ると多くの場合で優勢
- terser:swcに勝つこともある
- google-closure-compiler:向き不向きがありそう
- uglify-js:今は使われていないはず
→swcで色々試そうと思う
playground
設定を色々変えながら、試していく
DCE
in
const exp = false;
if(exp) {
console.log("1");
}
if(!exp) {
console.log("2");
}
out
console.log("2");
テンプレート
#
in
```ts
```
out
```ts
```
https://speakerdeck.com/mizchi/bandoruzui-shi-hua-maniakusu-at-tfconf?slide=22
in
const long_long_name_1: string = 'a';
const long_long_name_2: string = 'b';
export const exported_name_is_not_shrinkable = long_long_name_1 + long_long_name_2;
out
export const exported_name_is_not_shrinkable = "ab";
https://speakerdeck.com/mizchi/bandoruzui-shi-hua-maniakusu-at-tfconf?slide=23
in
const x = {
_private_value: 1,
f() {
return this._private_value;
},
unused_prop2: 2,
unused_prop3: 3,
};
export const f = x.f;
out
export const f = {
_private_value: 1,
f () {
return this._private_value;
},
unused_prop2: 2,
unused_prop3: 3
}.f;
https://speakerdeck.com/mizchi/bandoruzui-shi-hua-maniakusu-at-tfconf?slide=28
in
enum COLOR {
RED,
GREEN,
BLUE
}
const STOP_COLOR = COLOR.RED;
console.log(STOP_COLOR);
out
var COLOR;
!function(O) {
O[O.RED = 0] = "RED", O[O.GREEN = 1] = "GREEN", O[O.BLUE = 2] = "BLUE";
}(COLOR || (COLOR = {}));
const STOP_COLOR = COLOR.RED;
console.log(STOP_COLOR);
https://speakerdeck.com/mizchi/bandoruzui-shi-hua-maniakusu-at-tfconf?slide=29
in
- enum COLOR {
+ const enum COLOR {
RED,
GREEN,
BLUE
}
const STOP_COLOR = COLOR.RED;
console.log(STOP_COLOR);
out(preserveConstEnums: true)
"use strict";
var COLOR;
(function (COLOR) {
COLOR[COLOR["RED"] = 0] = "RED";
COLOR[COLOR["GREEN"] = 1] = "GREEN";
COLOR[COLOR["BLUE"] = 2] = "BLUE";
})(COLOR || (COLOR = {}));
const STOP_COLOR = 0 /* COLOR.RED */;
console.log(STOP_COLOR);
out(preserveConstEnums: false)
"use strict";
const STOP_COLOR = 0 /* COLOR.RED */;
console.log(STOP_COLOR);
enum使うと爆発するのは知ってたけど、const enum
にしてpreserveConstEnums: falseならばenumでもOKなのか
in
const enum COLOR {
RED,
GREEN,
BLUE
}
console.log(COLOR[COLOR.BLUE]);
out(preserveConstEnums: false)
"use strict";
console.log(COLOR[2 /* COLOR.BLUE */]);
でもこういう書き方するとダメなのか
tslib
存在は知ってるけど、なんであるのかは知らないので調べる
本体
tldr
- target: es2017 以降なら tslib 使っても置き換えられるコードはないので、使う必要はない
- target: es5/es2015 で async await を多く使っている場合はファイル数に比例して削れる
なるほど
「import * as を避ける」べきなのか
「import * as」の名前を調べようと思ったらアンサー記事が見つかってしまった
一応自分でも確かめる
cloneしてyarn install && yarn buildすると、webpackでbundleしたものと、swcでminifyしたものが出る
次はその様子
$ yarn build
yarn run v1.22.19
$ yarn bundle && yarn minify
$ webpack build
$ swc bundled -d minified
Successfully compiled: 2 files with swc (89.97ms)
✨ Done in 3.68s.
$ find bundled minified -type f -exec sh -c "ls -l {}" \;
-rw-r--r-- 1 kazu staff 115395 5 27 01:33 bundled/alias.js
-rw-r--r-- 1 kazu staff 115399 5 27 01:33 bundled/named.js
-rw-r--r-- 1 kazu staff 25950 5 27 01:46 minified/alias.js
-rw-r--r-- 1 kazu staff 25954 5 27 01:46 minified/named.js
ファイルサイズを比較するとほとんど一緒(4文字だけ違っている)
結果的に、「import * as」は常に避けるべきではなさそう
問題になるとしたらここだけ?
次やること
- https://terser.org/docs/api-reference を読む
- ReactのUIライブラリ系でやりがちなバンドルサイズを膨らませる方法とその対策を調べる