import * as 構文とパフォーマンス最適化の記事を読んだ
import * as 構文とパフォーマンス最適化 - Qiita
読んだ。
import * as
はTree Shakingが効かないという話はたしかにどこかで見たことがあった。それもあって基本的に使わないようにしてた(単純に有効に使える場面にあまり遭遇しなかったのかも)
Webpackの出力結果で比較してちゃんとTree Shakingが効いていることを確認していて、事実としてできているのでとても納得しやすい。
import * as構文を使ってもtree shakingが効くし、export名のmanglingも行われるということです。manglingというのは、(JavaScriptの文脈では)変数名などを短く書き直してコードサイズを減らすことを意味します。
manglingはなんとなく単語を見たことがある程度だったのだが、minifyなどで変数名や関数名を短くしたりすることなのか。学びです。
今回の例ではconsole.log(mod)では{ "foo": "foo", "bar": "bar" }という結果になることが期待されます。この結果を維持するために、エクスポートされる名前を変えたり減らしたりすることはできませんでした。また、このようにmodを直接参照されると、webpackのあずかり知らぬところでのちのちbarを使われたりする可能性を捨て切れません。そのため、tree shakingが行えなくなります。
一方で、console.log(mod.foo)のようにimport * asで得たmodに対してすぐプロパティアクセスする場合には、中身さえ同じならばconsole.log(mod.R)でも変わりません。webpackはこれを理解して最適化を行うのです。
つまり、export名を変えられたり消されたりしてもコードの意味が変わらないようにすれば、import * as構文を用いても最適化をしてもらうことができるのです。
具体的に言えば、import * as modのように得たモジュール名前空間オブジェクトは常にmod.fooのようにプロパティアクセスの形で使えば大丈夫です。
ここの文で完全に理解した。
プロパティアクセスの形で使わないで名前空間そのものにアクセスすると、エクスポートされている部分を変更するとコードの意味が変わってしまうということか(たぶん)
あるモジュールが何という名前の変数(バインディング)をエクスポートするのかということは、モジュールを実際に実行しなくても、モジュールを構文解析するだけで決定可能なのです。
これにより、import * as modのようにして得たmodがどんなプロパティを持っているかということも、静的解析により決定可能になります。それゆえに、mod.fooというプロパティアクセスの構文を使っている限り、mod.fooがインポートされたモジュールのどの変数に対応するかも追跡可能です。エクスポート名のmanglingもこのことを理論的裏付けとして行われています。
なるほど……。
静的解析できるからこそのmanglingだったりするわけですね。考えたこともなかった……。
つまり、import/exportをECMAScript仕様にしたがって解決するのはwebpackの役割なのです。だからこそ、webpackを通すとimport/exportはコードから消えてwebpackのランタイムに置き換えられます。これは、import/exportの解決という部分について、webpackは部分的にECMAScriptの実行環境として振舞っているということです。それゆえに、この部分に対してwebpackには好きなように最適化する権限が与えられます。
WebpackがECMAScriptの仕様に合わせてimport/exportを解決してるわけですね。
エンジニアリングにおいて「気をつける」というのはまともな解決策ではありません。ちゃんと仕組みで解決しましょう。
本当にそのとおりだと思うし、自分が気をつけるだけで解決しているときが全くないという自信がないので深く心に刻まれました。
uhyo/eslint-plugin-tree-shakable
解決する仕組みまで作ってあるなんてすごすぎる。uhyoさん本当にすごい。