Nuxt(Vue)の非同期コンポーネントはどうあるべきかの考察

Published: 2022/5/22


背景

Nuxt というよりその内部で使われている Vue は、非同期コンポーネントによって、コンポーネントロジックのロードを、実際にそのコンポーネントが必要になるまで遅延させることができる。 もし、非同期コンポーネントがリスクフリーであるならば、思考停止ですべてのコンポーネントを非同期にしてしまえば、後は勝手に Load by Need みたいなことが実現され、幸福が実現することになる。

果たして、それは本当なのか、そうでないならば、どの程度を非同期コンポーネントとするべきか、基準がイマイチ明確じゃないと感じたので、それについて考えた結果を dump する。

webpack と splitChunk

webpack では、最初に指定される entrypoint に加えて、ソースコード中に動的 import が表われれば、それを非同期モジュールとして認識し、 splitChunk 的な処理でいうと、ほぼ entrypoint と同じような扱いになる。 その結果どうなるかというと、動的 import が行われたモジュールに対しては、その他の場所で静的な import があったとしても、その静的な import を行っている chunk には、このモジュールは含まれないことになり、この component が生成する非同期 chunk に依存する、という整理になる。

splitChunkPlugin は、少なくともそのデフォルトの設定では、 chunk 間の共通コードは、ある程度の大きさがないとそれを splitChunk してくれない。 (細かい、例えば関数1つだけでも splitChunk などは、設定をすればおそらく実現できなくはないが、今度は module の依存読み込みオーバーヘッドの方が増えてしまう)

であるならば、動的 import を行う際には、実装しているアプリの本来的な要求として、その import は、それによって単体で import される場合とされない場合があり、それはその他アプリ中のすべての動的 import とは独立している、という性質がある場合に限り、これを行うのが良い、と考えられる。

具体的には、例えば PageA が ComponentA1, ComponentA2 を、 PageB が ComponentB1, ComponentB2 を import していたとする。 そして、 Component A1 と ComponentB1, そして ComponentA2 と ComponentB2 にそれぞれ共通コードがあり、これらを全部合わせると splitChunk できるぐらいの量になっていたとする。 もし、この時の import が非同期 import であった場合、 Component たちはそれぞれが独自の chunk を持つことになる。 そして、 A1 と A2、および B1 と B2 がそれぞれ同じ chunk に属していた場合、これは splitChunk によって共通コードが分離できるが、 A1 と B1, ないし A2 と B2 だけでは、 splitChunk ができなかったような場合には、 splitChunk が行われない。 特に javascript のサイズは、できるかぎり減らした方が良いことを考えると、不必要に細かくしてしまうと、本来効いたはずの splitChunk が効かなくなる、ということになるので、これは避けた方が良いと考えられる。

主張

本来同じタイミングでしか利用されないコードは、同じ chunk に存在するべき。

方法: slot の活用

あるページにおいて、本来的に同じタイミングで render されるはずなのに別々に動的 import している場合、それは、その動的 import を行っている Component に、いくつか切り出せるはずのロジックが同居してしまっている、と考えられる。 アンチパターンの語彙で言えば、 God Component 的な兆候である可能性が高い。

対処方法としては、 slot を活用するなどして、共通ロジックを切り出し、本質的に同じタイミングで import するべき component たちは、静的に component 登録するのが良い。

補足: Nuxt Components

ここまで述べた方針を実施する場合、 import 文を記述せずともファイル名称から規約に従いコンポーネント名称を導出し、それをグローバル登録してくれる Nuxt Components とは相性が悪い。

何故なら、このモジュールは、その管理対象となったすべての component を動的 import するようなコードをグローバルに記述する。 なので、どう頑張っても、すべての component がそれぞれ chunk を持つことになってしまう。 対処方法としては、 Nuxt Components はその管理対象のディレクトリを設定により指定できるので、動的 import を行いたいものは明示的に Nuxt Component で管理し、そうでないものは別の所で管理すると、良いのかもしれない。


Tags: nuxtvuewebpack

関連記事